aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2019-02-18 23:16:47 +0100
committerMichael Muré <batolettre@gmail.com>2019-03-01 22:40:26 +0100
commit54f9838f0ab22ce5285f21cdd117ad81c737d822 (patch)
treef6439599dec013d8f30620b5a534a1ebdb8f7388
parent947ea63522610bd16c32cf70812c129eda9bbb02 (diff)
downloadgit-bug-54f9838f0ab22ce5285f21cdd117ad81c737d822.tar.gz
identity: working identity cache
-rw-r--r--cache/bug_excerpt.go35
-rw-r--r--cache/filter.go44
-rw-r--r--cache/identity_cache.go17
-rw-r--r--cache/repo_cache.go137
-rw-r--r--commands/user.go2
-rw-r--r--identity/identity.go2
6 files changed, 204 insertions, 33 deletions
diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go
index d3645322..e39c8310 100644
--- a/cache/bug_excerpt.go
+++ b/cache/bug_excerpt.go
@@ -4,6 +4,7 @@ import (
"encoding/gob"
"github.com/MichaelMure/git-bug/bug"
+ "github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/lamport"
)
@@ -17,25 +18,49 @@ type BugExcerpt struct {
CreateUnixTime int64
EditUnixTime int64
- Status bug.Status
- AuthorId string
- Labels []bug.Label
+ Status bug.Status
+ Labels []bug.Label
+
+ // If author is identity.Bare, LegacyAuthor is set
+ // If author is identity.Identity, AuthorId is set and data is deported
+ // in a IdentityExcerpt
+ LegacyAuthor LegacyAuthorExcerpt
+ AuthorId string
CreateMetadata map[string]string
}
+// identity.Bare data are directly embedded in the bug excerpt
+type LegacyAuthorExcerpt struct {
+ Name string
+ Login string
+}
+
func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
- return &BugExcerpt{
+ e := &BugExcerpt{
Id: b.Id(),
CreateLamportTime: b.CreateLamportTime(),
EditLamportTime: b.EditLamportTime(),
CreateUnixTime: b.FirstOp().GetUnixTime(),
EditUnixTime: snap.LastEditUnix(),
Status: snap.Status,
- AuthorId: snap.Author.Id(),
Labels: snap.Labels,
CreateMetadata: b.FirstOp().AllMetadata(),
}
+
+ switch snap.Author.(type) {
+ case *identity.Identity:
+ e.AuthorId = snap.Author.Id()
+ case *identity.Bare:
+ e.LegacyAuthor = LegacyAuthorExcerpt{
+ Login: snap.Author.Login(),
+ Name: snap.Author.Name(),
+ }
+ default:
+ panic("unhandled identity type")
+ }
+
+ return e
}
// Package initialisation used to register the type for (de)serialization
diff --git a/cache/filter.go b/cache/filter.go
index 3cf4a991..3cbc132a 100644
--- a/cache/filter.go
+++ b/cache/filter.go
@@ -7,7 +7,7 @@ import (
)
// Filter is a functor that match a subset of bugs
-type Filter func(excerpt *BugExcerpt) bool
+type Filter func(repoCache *RepoCache, excerpt *BugExcerpt) bool
// StatusFilter return a Filter that match a bug status
func StatusFilter(query string) (Filter, error) {
@@ -16,24 +16,36 @@ func StatusFilter(query string) (Filter, error) {
return nil, err
}
- return func(excerpt *BugExcerpt) bool {
+ return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
return excerpt.Status == status
}, nil
}
// AuthorFilter return a Filter that match a bug author
func AuthorFilter(query string) Filter {
- return func(excerpt *BugExcerpt) bool {
+ return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
query = strings.ToLower(query)
- return strings.Contains(strings.ToLower(excerpt.Author.Name), query) ||
- strings.Contains(strings.ToLower(excerpt.Author.Login), query)
+ // Normal identity
+ if excerpt.AuthorId != "" {
+ author, ok := repoCache.identitiesExcerpts[excerpt.AuthorId]
+ if !ok {
+ panic("missing identity in the cache")
+ }
+
+ return strings.Contains(strings.ToLower(author.Name), query) ||
+ strings.Contains(strings.ToLower(author.Login), query)
+ }
+
+ // Legacy identity support
+ return strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Name), query) ||
+ strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Login), query)
}
}
// LabelFilter return a Filter that match a label
func LabelFilter(label string) Filter {
- return func(excerpt *BugExcerpt) bool {
+ return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
for _, l := range excerpt.Labels {
if string(l) == label {
return true
@@ -45,7 +57,7 @@ func LabelFilter(label string) Filter {
// NoLabelFilter return a Filter that match the absence of labels
func NoLabelFilter() Filter {
- return func(excerpt *BugExcerpt) bool {
+ return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
return len(excerpt.Labels) == 0
}
}
@@ -59,20 +71,20 @@ type Filters struct {
}
// Match check if a bug match the set of filters
-func (f *Filters) Match(excerpt *BugExcerpt) bool {
- if match := f.orMatch(f.Status, excerpt); !match {
+func (f *Filters) Match(repoCache *RepoCache, excerpt *BugExcerpt) bool {
+ if match := f.orMatch(f.Status, repoCache, excerpt); !match {
return false
}
- if match := f.orMatch(f.Author, excerpt); !match {
+ if match := f.orMatch(f.Author, repoCache, excerpt); !match {
return false
}
- if match := f.orMatch(f.Label, excerpt); !match {
+ if match := f.orMatch(f.Label, repoCache, excerpt); !match {
return false
}
- if match := f.andMatch(f.NoFilters, excerpt); !match {
+ if match := f.andMatch(f.NoFilters, repoCache, excerpt); !match {
return false
}
@@ -80,28 +92,28 @@ func (f *Filters) Match(excerpt *BugExcerpt) bool {
}
// Check if any of the filters provided match the bug
-func (*Filters) orMatch(filters []Filter, excerpt *BugExcerpt) bool {
+func (*Filters) orMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
if len(filters) == 0 {
return true
}
match := false
for _, f := range filters {
- match = match || f(excerpt)
+ match = match || f(repoCache, excerpt)
}
return match
}
// Check if all of the filters provided match the bug
-func (*Filters) andMatch(filters []Filter, excerpt *BugExcerpt) bool {
+func (*Filters) andMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
if len(filters) == 0 {
return true
}
match := true
for _, f := range filters {
- match = match && f(excerpt)
+ match = match && f(repoCache, excerpt)
}
return match
diff --git a/cache/identity_cache.go b/cache/identity_cache.go
index c49e9519..2ae55f2d 100644
--- a/cache/identity_cache.go
+++ b/cache/identity_cache.go
@@ -21,10 +21,23 @@ func (i *IdentityCache) notifyUpdated() error {
return i.repoCache.identityUpdated(i.Identity.Id())
}
+func (i *IdentityCache) AddVersion(version *identity.Version) error {
+ i.Identity.AddVersion(version)
+ return i.notifyUpdated()
+}
+
func (i *IdentityCache) Commit() error {
- return i.Identity.Commit(i.repoCache.repo)
+ err := i.Identity.Commit(i.repoCache.repo)
+ if err != nil {
+ return err
+ }
+ return i.notifyUpdated()
}
func (i *IdentityCache) CommitAsNeeded() error {
- return i.Identity.CommitAsNeeded(i.repoCache.repo)
+ err := i.Identity.CommitAsNeeded(i.repoCache.repo)
+ if err != nil {
+ return err
+ }
+ return i.notifyUpdated()
}
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index cec6f8b5..d5768125 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -27,6 +27,14 @@ const identityCacheFile = "identity-cache"
// 2: added cache for identities with a reference in the bug cache
const formatVersion = 2
+type ErrInvalidCacheFormat struct {
+ message string
+}
+
+func (e ErrInvalidCacheFormat) Error() string {
+ return e.message
+}
+
// RepoCache is a cache for a Repository. This cache has multiple functions:
//
// 1. After being loaded, a Bug is kept in memory in the cache, allowing for fast
@@ -75,6 +83,9 @@ func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) {
if err == nil {
return c, nil
}
+ if _, ok := err.(ErrInvalidCacheFormat); ok {
+ return nil, err
+ }
err = c.buildCache()
if err != nil {
@@ -156,7 +167,8 @@ func (c *RepoCache) bugUpdated(id string) error {
c.bugExcerpts[id] = NewBugExcerpt(b.bug, b.Snapshot())
- return c.write()
+ // we only need to write the bug cache
+ return c.writeBugCache()
}
// identityUpdated is a callback to trigger when the excerpt of an identity
@@ -169,11 +181,21 @@ func (c *RepoCache) identityUpdated(id string) error {
c.identitiesExcerpts[id] = NewIdentityExcerpt(i.Identity)
- return c.write()
+ // we only need to write the identity cache
+ return c.writeIdentityCache()
}
-// load will try to read from the disk the bug cache file
+// load will try to read from the disk all the cache files
func (c *RepoCache) load() error {
+ err := c.loadBugCache()
+ if err != nil {
+ return err
+ }
+ return c.loadIdentityCache()
+}
+
+// load will try to read from the disk the bug cache file
+func (c *RepoCache) loadBugCache() error {
f, err := os.Open(bugCacheFilePath(c.repo))
if err != nil {
return err
@@ -191,16 +213,56 @@ func (c *RepoCache) load() error {
return err
}
- if aux.Version != 1 {
- return fmt.Errorf("unknown cache format version %v", aux.Version)
+ if aux.Version != 2 {
+ return ErrInvalidCacheFormat{
+ message: fmt.Sprintf("unknown cache format version %v", aux.Version),
+ }
}
c.bugExcerpts = aux.Excerpts
return nil
}
-// write will serialize on disk the bug cache file
+// load will try to read from the disk the identity cache file
+func (c *RepoCache) loadIdentityCache() error {
+ f, err := os.Open(identityCacheFilePath(c.repo))
+ if err != nil {
+ return err
+ }
+
+ decoder := gob.NewDecoder(f)
+
+ aux := struct {
+ Version uint
+ Excerpts map[string]*IdentityExcerpt
+ }{}
+
+ err = decoder.Decode(&aux)
+ if err != nil {
+ return err
+ }
+
+ if aux.Version != 2 {
+ return ErrInvalidCacheFormat{
+ message: fmt.Sprintf("unknown cache format version %v", aux.Version),
+ }
+ }
+
+ c.identitiesExcerpts = aux.Excerpts
+ return nil
+}
+
+// write will serialize on disk all the cache files
func (c *RepoCache) write() error {
+ err := c.writeBugCache()
+ if err != nil {
+ return err
+ }
+ return c.writeIdentityCache()
+}
+
+// write will serialize on disk the bug cache file
+func (c *RepoCache) writeBugCache() error {
var data bytes.Buffer
aux := struct {
@@ -231,15 +293,63 @@ func (c *RepoCache) write() error {
return f.Close()
}
+// write will serialize on disk the identity cache file
+func (c *RepoCache) writeIdentityCache() error {
+ var data bytes.Buffer
+
+ aux := struct {
+ Version uint
+ Excerpts map[string]*IdentityExcerpt
+ }{
+ Version: formatVersion,
+ Excerpts: c.identitiesExcerpts,
+ }
+
+ encoder := gob.NewEncoder(&data)
+
+ err := encoder.Encode(aux)
+ if err != nil {
+ return err
+ }
+
+ f, err := os.Create(identityCacheFilePath(c.repo))
+ if err != nil {
+ return err
+ }
+
+ _, err = f.Write(data.Bytes())
+ if err != nil {
+ return err
+ }
+
+ return f.Close()
+}
+
func bugCacheFilePath(repo repository.Repo) string {
return path.Join(repo.GetPath(), ".git", "git-bug", bugCacheFile)
}
func identityCacheFilePath(repo repository.Repo) string {
- return path.Join(repo.GetPath(), ".git", "git-bug", bugCacheFile)
+ return path.Join(repo.GetPath(), ".git", "git-bug", identityCacheFile)
}
func (c *RepoCache) buildCache() error {
+ _, _ = fmt.Fprintf(os.Stderr, "Building identity cache... ")
+
+ c.identitiesExcerpts = make(map[string]*IdentityExcerpt)
+
+ allIdentities := identity.ReadAllLocalIdentities(c.repo)
+
+ for i := range allIdentities {
+ if i.Err != nil {
+ return i.Err
+ }
+
+ c.identitiesExcerpts[i.Identity.Id()] = NewIdentityExcerpt(i.Identity)
+ }
+
+ _, _ = fmt.Fprintln(os.Stderr, "Done.")
+
_, _ = fmt.Fprintf(os.Stderr, "Building bug cache... ")
c.bugExcerpts = make(map[string]*BugExcerpt)
@@ -333,7 +443,7 @@ func (c *RepoCache) QueryBugs(query *Query) []string {
var filtered []*BugExcerpt
for _, excerpt := range c.bugExcerpts {
- if query.Match(excerpt) {
+ if query.Match(c, excerpt) {
filtered = append(filtered, excerpt)
}
}
@@ -463,11 +573,15 @@ func (c *RepoCache) NewBugRaw(author *IdentityCache, unixTime int64, title strin
// Fetch retrieve update from a remote
// This does not change the local bugs state
func (c *RepoCache) Fetch(remote string) (string, error) {
+ // TODO: add identities
+
return bug.Fetch(c.repo, remote)
}
// MergeAll will merge all the available remote bug
func (c *RepoCache) MergeAll(remote string) <-chan bug.MergeResult {
+ // TODO: add identities
+
out := make(chan bug.MergeResult)
// Intercept merge results to update the cache properly
@@ -505,6 +619,8 @@ func (c *RepoCache) MergeAll(remote string) <-chan bug.MergeResult {
// Push update a remote with the local changes
func (c *RepoCache) Push(remote string) (string, error) {
+ // TODO: add identities
+
return bug.Push(c.repo, remote)
}
@@ -655,6 +771,11 @@ func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
return err
}
+ // Make sure that everything is fine
+ if _, ok := c.identities[i.Id()]; !ok {
+ panic("SetUserIdentity while the identity is not from the cache, something is wrong")
+ }
+
c.userIdentityId = i.Id()
return nil
diff --git a/commands/user.go b/commands/user.go
index 55ad813c..e7a1da63 100644
--- a/commands/user.go
+++ b/commands/user.go
@@ -38,7 +38,7 @@ func runUser(cmd *cobra.Command, args []string) error {
fmt.Printf("Name: %s\n", id.Name())
fmt.Printf("Login: %s\n", id.Login())
fmt.Printf("Email: %s\n", id.Email())
- fmt.Printf("Protected: %v\n", id.IsProtected())
+ // fmt.Printf("Protected: %v\n", id.IsProtected())
return nil
}
diff --git a/identity/identity.go b/identity/identity.go
index 35edca18..193a3013 100644
--- a/identity/identity.go
+++ b/identity/identity.go
@@ -84,7 +84,7 @@ func ReadRemote(repo repository.Repo, remote string, id string) (*Identity, erro
return read(repo, ref)
}
-// read will load and parse an identity frdm git
+// read will load and parse an identity from git
func read(repo repository.Repo, ref string) (*Identity, error) {
refSplit := strings.Split(ref, "/")
id := refSplit[len(refSplit)-1]