From feab9412dffe5772048aad29893c4cb01d566387 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Wed, 21 Nov 2018 18:56:12 +0100 Subject: WIP identity in git --- cache/bug_cache.go | 26 ++++++++++++++------------ cache/bug_excerpt.go | 4 +++- cache/repo_cache.go | 5 +++-- 3 files changed, 20 insertions(+), 15 deletions(-) (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 52e9eafb..25ff000c 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -5,6 +5,8 @@ import ( "strings" "time" + "github.com/MichaelMure/git-bug/identity" + "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/util/git" ) @@ -87,7 +89,7 @@ func (c *BugCache) AddComment(message string) error { } func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error { - author, err := bug.GetUser(c.repoCache.repo) + author, err := identity.GetIdentity(c.repoCache.repo) if err != nil { return err } @@ -95,7 +97,7 @@ func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error { return c.AddCommentRaw(author, time.Now().Unix(), message, files, nil) } -func (c *BugCache) AddCommentRaw(author bug.Person, unixTime int64, message string, files []git.Hash, metadata map[string]string) error { +func (c *BugCache) AddCommentRaw(author *identity.Identity, unixTime int64, message string, files []git.Hash, metadata map[string]string) error { op, err := bug.AddCommentWithFiles(c.bug, author, unixTime, message, files) if err != nil { return err @@ -109,7 +111,7 @@ func (c *BugCache) AddCommentRaw(author bug.Person, unixTime int64, message stri } func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelChangeResult, error) { - author, err := bug.GetUser(c.repoCache.repo) + author, err := identity.GetIdentity(c.repoCache.repo) if err != nil { return nil, err } @@ -117,7 +119,7 @@ func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelCh return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil) } -func (c *BugCache) ChangeLabelsRaw(author bug.Person, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) { +func (c *BugCache) ChangeLabelsRaw(author *identity.Identity, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) { changes, op, err := bug.ChangeLabels(c.bug, author, unixTime, added, removed) if err != nil { return changes, err @@ -136,7 +138,7 @@ func (c *BugCache) ChangeLabelsRaw(author bug.Person, unixTime int64, added []st } func (c *BugCache) Open() error { - author, err := bug.GetUser(c.repoCache.repo) + author, err := identity.GetIdentity(c.repoCache.repo) if err != nil { return err } @@ -144,7 +146,7 @@ func (c *BugCache) Open() error { return c.OpenRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) OpenRaw(author bug.Person, unixTime int64, metadata map[string]string) error { +func (c *BugCache) OpenRaw(author *identity.Identity, unixTime int64, metadata map[string]string) error { op, err := bug.Open(c.bug, author, unixTime) if err != nil { return err @@ -158,7 +160,7 @@ func (c *BugCache) OpenRaw(author bug.Person, unixTime int64, metadata map[strin } func (c *BugCache) Close() error { - author, err := bug.GetUser(c.repoCache.repo) + author, err := identity.GetIdentity(c.repoCache.repo) if err != nil { return err } @@ -166,7 +168,7 @@ func (c *BugCache) Close() error { return c.CloseRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) CloseRaw(author bug.Person, unixTime int64, metadata map[string]string) error { +func (c *BugCache) CloseRaw(author *identity.Identity, unixTime int64, metadata map[string]string) error { op, err := bug.Close(c.bug, author, unixTime) if err != nil { return err @@ -180,7 +182,7 @@ func (c *BugCache) CloseRaw(author bug.Person, unixTime int64, metadata map[stri } func (c *BugCache) SetTitle(title string) error { - author, err := bug.GetUser(c.repoCache.repo) + author, err := identity.GetIdentity(c.repoCache.repo) if err != nil { return err } @@ -188,7 +190,7 @@ func (c *BugCache) SetTitle(title string) error { return c.SetTitleRaw(author, time.Now().Unix(), title, nil) } -func (c *BugCache) SetTitleRaw(author bug.Person, unixTime int64, title string, metadata map[string]string) error { +func (c *BugCache) SetTitleRaw(author *identity.Identity, unixTime int64, title string, metadata map[string]string) error { op, err := bug.SetTitle(c.bug, author, unixTime, title) if err != nil { return err @@ -202,7 +204,7 @@ func (c *BugCache) SetTitleRaw(author bug.Person, unixTime int64, title string, } func (c *BugCache) EditComment(target git.Hash, message string) error { - author, err := bug.GetUser(c.repoCache.repo) + author, err := identity.GetIdentity(c.repoCache.repo) if err != nil { return err } @@ -210,7 +212,7 @@ func (c *BugCache) EditComment(target git.Hash, message string) error { return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil) } -func (c *BugCache) EditCommentRaw(author bug.Person, unixTime int64, target git.Hash, message string, metadata map[string]string) error { +func (c *BugCache) EditCommentRaw(author *identity.Identity, unixTime int64, target git.Hash, message string, metadata map[string]string) error { op, err := bug.EditComment(c.bug, author, unixTime, target, message) if err != nil { return err diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index 7a11fa82..77c18175 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -3,6 +3,8 @@ package cache import ( "encoding/gob" + "github.com/MichaelMure/git-bug/identity" + "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/util/lamport" ) @@ -18,7 +20,7 @@ type BugExcerpt struct { EditUnixTime int64 Status bug.Status - Author bug.Person + Author *identity.Identity Labels []bug.Label CreateMetadata map[string]string diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 286e27a5..a149fd73 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -14,6 +14,7 @@ import ( "time" "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/git" "github.com/MichaelMure/git-bug/util/process" @@ -376,7 +377,7 @@ func (c *RepoCache) NewBug(title string, message string) (*BugCache, error) { // NewBugWithFiles create a new bug with attached files for the message // The new bug is written in the repository (commit) func (c *RepoCache) NewBugWithFiles(title string, message string, files []git.Hash) (*BugCache, error) { - author, err := bug.GetUser(c.repo) + author, err := identity.GetIdentity(c.repo) if err != nil { return nil, err } @@ -387,7 +388,7 @@ func (c *RepoCache) NewBugWithFiles(title string, message string, files []git.Ha // NewBugWithFilesMeta create a new bug with attached files for the message, as // well as metadata for the Create operation. // The new bug is written in the repository (commit) -func (c *RepoCache) NewBugRaw(author bug.Person, unixTime int64, title string, message string, files []git.Hash, metadata map[string]string) (*BugCache, error) { +func (c *RepoCache) NewBugRaw(author *identity.Identity, unixTime int64, title string, message string, files []git.Hash, metadata map[string]string) (*BugCache, error) { b, op, err := bug.CreateWithFiles(author, unixTime, title, message, files) if err != nil { return nil, err -- cgit From bdbe9e7e8256fff820efe1ce707e7154d517ecb3 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Thu, 17 Jan 2019 03:09:08 +0100 Subject: identity: more progress and fixes --- cache/bug_excerpt.go | 2 +- cache/repo_cache.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) (limited to 'cache') diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index 77c18175..f5844b64 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -20,7 +20,7 @@ type BugExcerpt struct { EditUnixTime int64 Status bug.Status - Author *identity.Identity + Author identity.Interface Labels []bug.Label CreateMetadata map[string]string diff --git a/cache/repo_cache.go b/cache/repo_cache.go index a149fd73..7d3e7d1d 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -30,6 +30,8 @@ type RepoCache struct { excerpts map[string]*BugExcerpt // bug loaded in memory bugs map[string]*BugCache + // identities loaded in memory + identities map[string]*identity.Identity } func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) { @@ -279,6 +281,7 @@ func (c *RepoCache) ResolveBugCreateMetadata(key string, value string) (*BugCach return c.ResolveBug(matching[0]) } +// QueryBugs return the id of all Bug matching the given Query func (c *RepoCache) QueryBugs(query *Query) []string { if query == nil { return c.AllBugsIds() @@ -525,3 +528,70 @@ func repoIsAvailable(repo repository.Repo) error { return nil } + +// ResolveIdentity retrieve an identity matching the exact given id +func (c *RepoCache) ResolveIdentity(id string) (*identity.Identity, error) { + cached, ok := c.identities[id] + if ok { + return cached, nil + } + + i, err := identity.Read(c.repo, id) + if err != nil { + return nil, err + } + + c.identities[id] = i + + return i, nil +} + +// ResolveIdentityPrefix retrieve an Identity matching an id prefix. It fails if multiple +// bugs match. +// func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*BugCache, error) { +// // preallocate but empty +// matching := make([]string, 0, 5) +// +// for id := range c.excerpts { +// if strings.HasPrefix(id, prefix) { +// matching = append(matching, id) +// } +// } +// +// if len(matching) > 1 { +// return nil, bug.ErrMultipleMatch{Matching: matching} +// } +// +// if len(matching) == 0 { +// return nil, bug.ErrBugNotExist +// } +// +// return c.ResolveBug(matching[0]) +// } + +// ResolveIdentityImmutableMetadata retrieve an Identity that has the exact given metadata on +// one of it's version. If multiple version have the same key, the first defined take precedence. +func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (*BugCache, error) { + // // preallocate but empty + // matching := make([]string, 0, 5) + // + // for id, excerpt := range c.excerpts { + // if excerpt.CreateMetadata[key] == value { + // matching = append(matching, id) + // } + // } + // + // if len(matching) > 1 { + // return nil, bug.ErrMultipleMatch{Matching: matching} + // } + // + // if len(matching) == 0 { + // return nil, bug.ErrBugNotExist + // } + // + // return c.ResolveBug(matching[0]) + + // TODO + + return nil, nil +} -- cgit From 844616baf8dc628360942d57fd69f24e298e08da Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 19 Jan 2019 16:01:06 +0100 Subject: identity: more progress and fixes --- cache/bug_cache.go | 4 ++ cache/multi_repo_cache.go | 1 + cache/repo_cache.go | 138 +++++++++++++++++++++++++++++++--------------- 3 files changed, 98 insertions(+), 45 deletions(-) (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 25ff000c..53c5c7d9 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -11,6 +11,10 @@ import ( "github.com/MichaelMure/git-bug/util/git" ) +// BugCache is a wrapper around a Bug. It provide multiple functions: +// +// 1. Provide a higher level API to use than the raw API from Bug. +// 2. Maintain an up to date Snapshot available. type BugCache struct { repoCache *RepoCache bug *bug.WithSnapshot diff --git a/cache/multi_repo_cache.go b/cache/multi_repo_cache.go index ec435ff2..da1c26bd 100644 --- a/cache/multi_repo_cache.go +++ b/cache/multi_repo_cache.go @@ -8,6 +8,7 @@ import ( const lockfile = "lock" +// MultiRepoCache is the root cache, holding multiple RepoCache. type MultiRepoCache struct { repos map[string]*RepoCache } diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 7d3e7d1d..e1a3d8f8 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -23,6 +23,20 @@ import ( const cacheFile = "cache" const formatVersion = 1 +// 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 +// access later. +// 2. The cache maintain on memory and on disk a pre-digested excerpt for each bug, +// allowing for fast querying the whole set of bugs without having to load +// them individually. +// 3. The cache guarantee that a single instance of a Bug is loaded at once, avoiding +// loss of data that we could have with multiple copies in the same process. +// 4. The same way, the cache maintain in memory a single copy of the loaded identities. +// +// The cache also protect the on-disk data by locking the git repository for its +// own usage, by writing a lock file. Of course, normal git operations are not +// affected, only git-bug related one. type RepoCache struct { // the underlying repo repo repository.ClockedRepo @@ -406,9 +420,14 @@ func (c *RepoCache) NewBugRaw(author *identity.Identity, unixTime int64, title s return nil, err } + if _, has := c.bugs[b.Id()]; has { + return nil, fmt.Errorf("bug %s already exist in the cache", b.Id()) + } + cached := NewBugCache(c, b) c.bugs[b.Id()] = cached + // force the write of the excerpt err = c.bugUpdated(b.Id()) if err != nil { return nil, err @@ -546,52 +565,81 @@ func (c *RepoCache) ResolveIdentity(id string) (*identity.Identity, error) { return i, nil } -// ResolveIdentityPrefix retrieve an Identity matching an id prefix. It fails if multiple -// bugs match. -// func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*BugCache, error) { -// // preallocate but empty -// matching := make([]string, 0, 5) -// -// for id := range c.excerpts { -// if strings.HasPrefix(id, prefix) { -// matching = append(matching, id) -// } -// } -// -// if len(matching) > 1 { -// return nil, bug.ErrMultipleMatch{Matching: matching} -// } -// -// if len(matching) == 0 { -// return nil, bug.ErrBugNotExist -// } -// -// return c.ResolveBug(matching[0]) -// } +// ResolveIdentityPrefix retrieve an Identity matching an id prefix. +// It fails if multiple identities match. +func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*identity.Identity, error) { + // preallocate but empty + matching := make([]string, 0, 5) + + for id := range c.identities { + if strings.HasPrefix(id, prefix) { + matching = append(matching, id) + } + } + + if len(matching) > 1 { + return nil, identity.ErrMultipleMatch{Matching: matching} + } + + if len(matching) == 0 { + return nil, identity.ErrIdentityNotExist + } + + return c.ResolveIdentity(matching[0]) +} // ResolveIdentityImmutableMetadata retrieve an Identity that has the exact given metadata on // one of it's version. If multiple version have the same key, the first defined take precedence. -func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (*BugCache, error) { - // // preallocate but empty - // matching := make([]string, 0, 5) - // - // for id, excerpt := range c.excerpts { - // if excerpt.CreateMetadata[key] == value { - // matching = append(matching, id) - // } - // } - // - // if len(matching) > 1 { - // return nil, bug.ErrMultipleMatch{Matching: matching} - // } - // - // if len(matching) == 0 { - // return nil, bug.ErrBugNotExist - // } - // - // return c.ResolveBug(matching[0]) - - // TODO - - return nil, nil +func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (*identity.Identity, error) { + // preallocate but empty + matching := make([]string, 0, 5) + + for id, i := range c.identities { + if i.ImmutableMetadata()[key] == value { + matching = append(matching, id) + } + } + + if len(matching) > 1 { + return nil, identity.ErrMultipleMatch{Matching: matching} + } + + if len(matching) == 0 { + return nil, identity.ErrIdentityNotExist + } + + return c.ResolveIdentity(matching[0]) +} + +// NewIdentity create a new identity +// The new identity is written in the repository (commit) +func (c *RepoCache) NewIdentity(name string, email string) (*identity.Identity, error) { + return c.NewIdentityRaw(name, email, "", "", 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) (*identity.Identity, error) { + return c.NewIdentityRaw(name, email, login, avatarUrl, nil) +} + +func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avatarUrl string, metadata map[string]string) (*identity.Identity, error) { + i := identity.NewIdentityFull(name, email, login, avatarUrl) + + for key, value := range metadata { + i.SetMetadata(key, value) + } + + err := i.Commit(c.repo) + if err != nil { + return nil, err + } + + if _, has := c.identities[i.Id()]; has { + return nil, fmt.Errorf("identity %s already exist in the cache", i.Id()) + } + + c.identities[i.Id()] = i + + return i, nil } -- cgit From 14b240af8fef269d2c1d5dde2fff192b656c50f3 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 20 Jan 2019 15:41:27 +0100 Subject: identity: more cleaning and fixes after a code review --- cache/bug_cache.go | 12 ++++++------ cache/bug_excerpt.go | 18 ++++++++++++------ cache/filter.go | 7 ++++++- cache/repo_cache.go | 2 +- 4 files changed, 25 insertions(+), 14 deletions(-) (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 53c5c7d9..53c1db64 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -93,7 +93,7 @@ func (c *BugCache) AddComment(message string) error { } func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error { - author, err := identity.GetIdentity(c.repoCache.repo) + author, err := identity.GetUserIdentity(c.repoCache.repo) if err != nil { return err } @@ -115,7 +115,7 @@ func (c *BugCache) AddCommentRaw(author *identity.Identity, unixTime int64, mess } func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelChangeResult, error) { - author, err := identity.GetIdentity(c.repoCache.repo) + author, err := identity.GetUserIdentity(c.repoCache.repo) if err != nil { return nil, err } @@ -142,7 +142,7 @@ func (c *BugCache) ChangeLabelsRaw(author *identity.Identity, unixTime int64, ad } func (c *BugCache) Open() error { - author, err := identity.GetIdentity(c.repoCache.repo) + author, err := identity.GetUserIdentity(c.repoCache.repo) if err != nil { return err } @@ -164,7 +164,7 @@ func (c *BugCache) OpenRaw(author *identity.Identity, unixTime int64, metadata m } func (c *BugCache) Close() error { - author, err := identity.GetIdentity(c.repoCache.repo) + author, err := identity.GetUserIdentity(c.repoCache.repo) if err != nil { return err } @@ -186,7 +186,7 @@ func (c *BugCache) CloseRaw(author *identity.Identity, unixTime int64, metadata } func (c *BugCache) SetTitle(title string) error { - author, err := identity.GetIdentity(c.repoCache.repo) + author, err := identity.GetUserIdentity(c.repoCache.repo) if err != nil { return err } @@ -208,7 +208,7 @@ func (c *BugCache) SetTitleRaw(author *identity.Identity, unixTime int64, title } func (c *BugCache) EditComment(target git.Hash, message string) error { - author, err := identity.GetIdentity(c.repoCache.repo) + author, err := identity.GetUserIdentity(c.repoCache.repo) if err != nil { return err } diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index f5844b64..daf89c4f 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -3,8 +3,6 @@ package cache import ( "encoding/gob" - "github.com/MichaelMure/git-bug/identity" - "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/util/lamport" ) @@ -20,12 +18,17 @@ type BugExcerpt struct { EditUnixTime int64 Status bug.Status - Author identity.Interface + Author AuthorExcerpt Labels []bug.Label CreateMetadata map[string]string } +type AuthorExcerpt struct { + Name string + Login string +} + func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { return &BugExcerpt{ Id: b.Id(), @@ -34,9 +37,12 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { CreateUnixTime: b.FirstOp().GetUnixTime(), EditUnixTime: snap.LastEditUnix(), Status: snap.Status, - Author: snap.Author, - Labels: snap.Labels, - CreateMetadata: b.FirstOp().AllMetadata(), + Author: AuthorExcerpt{ + Login: snap.Author.Login(), + Name: snap.Author.Name(), + }, + Labels: snap.Labels, + CreateMetadata: b.FirstOp().AllMetadata(), } } diff --git a/cache/filter.go b/cache/filter.go index 033df131..3cf4a991 100644 --- a/cache/filter.go +++ b/cache/filter.go @@ -1,6 +1,8 @@ package cache import ( + "strings" + "github.com/MichaelMure/git-bug/bug" ) @@ -22,7 +24,10 @@ func StatusFilter(query string) (Filter, error) { // AuthorFilter return a Filter that match a bug author func AuthorFilter(query string) Filter { return func(excerpt *BugExcerpt) bool { - return excerpt.Author.Match(query) + query = strings.ToLower(query) + + return strings.Contains(strings.ToLower(excerpt.Author.Name), query) || + strings.Contains(strings.ToLower(excerpt.Author.Login), query) } } diff --git a/cache/repo_cache.go b/cache/repo_cache.go index e1a3d8f8..3d8b352b 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -394,7 +394,7 @@ func (c *RepoCache) NewBug(title string, message string) (*BugCache, error) { // NewBugWithFiles create a new bug with attached files for the message // The new bug is written in the repository (commit) func (c *RepoCache) NewBugWithFiles(title string, message string, files []git.Hash) (*BugCache, error) { - author, err := identity.GetIdentity(c.repo) + author, err := identity.GetUserIdentity(c.repo) if err != nil { return nil, err } -- cgit From 328a4e5abf3ec8ea41f89575fcfb83cf9f086c80 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 3 Feb 2019 19:55:35 +0100 Subject: identity: wip push/pull --- cache/repo_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cache') diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 3d8b352b..f207e984 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -555,7 +555,7 @@ func (c *RepoCache) ResolveIdentity(id string) (*identity.Identity, error) { return cached, nil } - i, err := identity.Read(c.repo, id) + i, err := identity.ReadLocal(c.repo, id) if err != nil { return nil, err } -- cgit From d2483d83dd52365741f51eca106aa18c4e8d6420 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 16 Feb 2019 17:32:30 +0100 Subject: identity: I can compile again !! --- cache/bug_cache.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 53c1db64..1d161c76 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -234,8 +234,5 @@ func (c *BugCache) Commit() error { } func (c *BugCache) CommitAsNeeded() error { - if c.bug.HasPendingOp() { - return c.bug.Commit(c.repoCache.repo) - } - return nil + return c.bug.CommitAsNeeded(c.repoCache.repo) } -- cgit From da558b05ef79f4c80df10c6969a9ae5f4f764f96 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 16 Feb 2019 19:34:52 +0100 Subject: identity: all tests green o/ --- cache/bug_cache.go | 12 ++++++------ cache/repo_cache.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 1d161c76..2a570667 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -101,7 +101,7 @@ func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error { return c.AddCommentRaw(author, time.Now().Unix(), message, files, nil) } -func (c *BugCache) AddCommentRaw(author *identity.Identity, unixTime int64, message string, files []git.Hash, metadata map[string]string) error { +func (c *BugCache) AddCommentRaw(author identity.Interface, unixTime int64, message string, files []git.Hash, metadata map[string]string) error { op, err := bug.AddCommentWithFiles(c.bug, author, unixTime, message, files) if err != nil { return err @@ -123,7 +123,7 @@ func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelCh return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil) } -func (c *BugCache) ChangeLabelsRaw(author *identity.Identity, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) { +func (c *BugCache) ChangeLabelsRaw(author identity.Interface, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) { changes, op, err := bug.ChangeLabels(c.bug, author, unixTime, added, removed) if err != nil { return changes, err @@ -150,7 +150,7 @@ func (c *BugCache) Open() error { return c.OpenRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) OpenRaw(author *identity.Identity, unixTime int64, metadata map[string]string) error { +func (c *BugCache) OpenRaw(author identity.Interface, unixTime int64, metadata map[string]string) error { op, err := bug.Open(c.bug, author, unixTime) if err != nil { return err @@ -172,7 +172,7 @@ func (c *BugCache) Close() error { return c.CloseRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) CloseRaw(author *identity.Identity, unixTime int64, metadata map[string]string) error { +func (c *BugCache) CloseRaw(author identity.Interface, unixTime int64, metadata map[string]string) error { op, err := bug.Close(c.bug, author, unixTime) if err != nil { return err @@ -194,7 +194,7 @@ func (c *BugCache) SetTitle(title string) error { return c.SetTitleRaw(author, time.Now().Unix(), title, nil) } -func (c *BugCache) SetTitleRaw(author *identity.Identity, unixTime int64, title string, metadata map[string]string) error { +func (c *BugCache) SetTitleRaw(author identity.Interface, unixTime int64, title string, metadata map[string]string) error { op, err := bug.SetTitle(c.bug, author, unixTime, title) if err != nil { return err @@ -216,7 +216,7 @@ func (c *BugCache) EditComment(target git.Hash, message string) error { return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil) } -func (c *BugCache) EditCommentRaw(author *identity.Identity, unixTime int64, target git.Hash, message string, metadata map[string]string) error { +func (c *BugCache) EditCommentRaw(author identity.Interface, unixTime int64, target git.Hash, message string, metadata map[string]string) error { op, err := bug.EditComment(c.bug, author, unixTime, target, message) if err != nil { return err diff --git a/cache/repo_cache.go b/cache/repo_cache.go index f207e984..74f04e11 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -405,7 +405,7 @@ func (c *RepoCache) NewBugWithFiles(title string, message string, files []git.Ha // NewBugWithFilesMeta create a new bug with attached files for the message, as // well as metadata for the Create operation. // The new bug is written in the repository (commit) -func (c *RepoCache) NewBugRaw(author *identity.Identity, unixTime int64, title string, message string, files []git.Hash, metadata map[string]string) (*BugCache, error) { +func (c *RepoCache) NewBugRaw(author identity.Interface, unixTime int64, title string, message string, files []git.Hash, metadata map[string]string) (*BugCache, error) { b, op, err := bug.CreateWithFiles(author, unixTime, title, message, files) if err != nil { return nil, err -- cgit From 864eae0d6bd0732260c0c56583bb77f9b25b60f6 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 17 Feb 2019 16:12:06 +0100 Subject: identity: work on higher level now, cache, first two identity commands --- cache/bug_cache.go | 38 +++++++++++++-------------- cache/identity_cache.go | 26 +++++++++++++++++++ cache/repo_cache.go | 68 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 96 insertions(+), 36 deletions(-) create mode 100644 cache/identity_cache.go (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 2a570667..ce46837a 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -5,8 +5,6 @@ import ( "strings" "time" - "github.com/MichaelMure/git-bug/identity" - "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/util/git" ) @@ -93,7 +91,7 @@ func (c *BugCache) AddComment(message string) error { } func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error { - author, err := identity.GetUserIdentity(c.repoCache.repo) + author, err := c.repoCache.GetUserIdentity() if err != nil { return err } @@ -101,8 +99,8 @@ func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error { return c.AddCommentRaw(author, time.Now().Unix(), message, files, nil) } -func (c *BugCache) AddCommentRaw(author identity.Interface, unixTime int64, message string, files []git.Hash, metadata map[string]string) error { - op, err := bug.AddCommentWithFiles(c.bug, author, unixTime, message, files) +func (c *BugCache) AddCommentRaw(author *IdentityCache, unixTime int64, message string, files []git.Hash, metadata map[string]string) error { + op, err := bug.AddCommentWithFiles(c.bug, author.Identity, unixTime, message, files) if err != nil { return err } @@ -115,7 +113,7 @@ func (c *BugCache) AddCommentRaw(author identity.Interface, unixTime int64, mess } func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelChangeResult, error) { - author, err := identity.GetUserIdentity(c.repoCache.repo) + author, err := c.repoCache.GetUserIdentity() if err != nil { return nil, err } @@ -123,8 +121,8 @@ func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelCh return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil) } -func (c *BugCache) ChangeLabelsRaw(author identity.Interface, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) { - changes, op, err := bug.ChangeLabels(c.bug, author, unixTime, added, removed) +func (c *BugCache) ChangeLabelsRaw(author *IdentityCache, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) { + changes, op, err := bug.ChangeLabels(c.bug, author.Identity, unixTime, added, removed) if err != nil { return changes, err } @@ -142,7 +140,7 @@ func (c *BugCache) ChangeLabelsRaw(author identity.Interface, unixTime int64, ad } func (c *BugCache) Open() error { - author, err := identity.GetUserIdentity(c.repoCache.repo) + author, err := c.repoCache.GetUserIdentity() if err != nil { return err } @@ -150,8 +148,8 @@ func (c *BugCache) Open() error { return c.OpenRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) OpenRaw(author identity.Interface, unixTime int64, metadata map[string]string) error { - op, err := bug.Open(c.bug, author, unixTime) +func (c *BugCache) OpenRaw(author *IdentityCache, unixTime int64, metadata map[string]string) error { + op, err := bug.Open(c.bug, author.Identity, unixTime) if err != nil { return err } @@ -164,7 +162,7 @@ func (c *BugCache) OpenRaw(author identity.Interface, unixTime int64, metadata m } func (c *BugCache) Close() error { - author, err := identity.GetUserIdentity(c.repoCache.repo) + author, err := c.repoCache.GetUserIdentity() if err != nil { return err } @@ -172,8 +170,8 @@ func (c *BugCache) Close() error { return c.CloseRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) CloseRaw(author identity.Interface, unixTime int64, metadata map[string]string) error { - op, err := bug.Close(c.bug, author, unixTime) +func (c *BugCache) CloseRaw(author *IdentityCache, unixTime int64, metadata map[string]string) error { + op, err := bug.Close(c.bug, author.Identity, unixTime) if err != nil { return err } @@ -186,7 +184,7 @@ func (c *BugCache) CloseRaw(author identity.Interface, unixTime int64, metadata } func (c *BugCache) SetTitle(title string) error { - author, err := identity.GetUserIdentity(c.repoCache.repo) + author, err := c.repoCache.GetUserIdentity() if err != nil { return err } @@ -194,8 +192,8 @@ func (c *BugCache) SetTitle(title string) error { return c.SetTitleRaw(author, time.Now().Unix(), title, nil) } -func (c *BugCache) SetTitleRaw(author identity.Interface, unixTime int64, title string, metadata map[string]string) error { - op, err := bug.SetTitle(c.bug, author, unixTime, title) +func (c *BugCache) SetTitleRaw(author *IdentityCache, unixTime int64, title string, metadata map[string]string) error { + op, err := bug.SetTitle(c.bug, author.Identity, unixTime, title) if err != nil { return err } @@ -208,7 +206,7 @@ func (c *BugCache) SetTitleRaw(author identity.Interface, unixTime int64, title } func (c *BugCache) EditComment(target git.Hash, message string) error { - author, err := identity.GetUserIdentity(c.repoCache.repo) + author, err := c.repoCache.GetUserIdentity() if err != nil { return err } @@ -216,8 +214,8 @@ func (c *BugCache) EditComment(target git.Hash, message string) error { return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil) } -func (c *BugCache) EditCommentRaw(author identity.Interface, unixTime int64, target git.Hash, message string, metadata map[string]string) error { - op, err := bug.EditComment(c.bug, author, unixTime, target, message) +func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target git.Hash, message string, metadata map[string]string) error { + op, err := bug.EditComment(c.bug, author.Identity, unixTime, target, message) if err != nil { return err } diff --git a/cache/identity_cache.go b/cache/identity_cache.go new file mode 100644 index 00000000..93b2dc4b --- /dev/null +++ b/cache/identity_cache.go @@ -0,0 +1,26 @@ +package cache + +import ( + "github.com/MichaelMure/git-bug/identity" +) + +// IdentityCache is a wrapper around an Identity. It provide multiple functions: +type IdentityCache struct { + *identity.Identity + repoCache *RepoCache +} + +func NewIdentityCache(repoCache *RepoCache, id *identity.Identity) *IdentityCache { + return &IdentityCache{ + Identity: id, + repoCache: repoCache, + } +} + +func (i *IdentityCache) Commit() error { + return i.Identity.Commit(i.repoCache.repo) +} + +func (i *IdentityCache) CommitAsNeeded() error { + return i.Identity.CommitAsNeeded(i.repoCache.repo) +} diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 74f04e11..f64a1b76 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -45,13 +45,16 @@ type RepoCache struct { // bug loaded in memory bugs map[string]*BugCache // identities loaded in memory - identities map[string]*identity.Identity + identities map[string]*IdentityCache + // the user identity's id, if known + userIdentityId string } func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) { c := &RepoCache{ - repo: r, - bugs: make(map[string]*BugCache), + repo: r, + bugs: make(map[string]*BugCache), + identities: make(map[string]*IdentityCache), } err := c.lock() @@ -394,7 +397,7 @@ func (c *RepoCache) NewBug(title string, message string) (*BugCache, error) { // NewBugWithFiles create a new bug with attached files for the message // The new bug is written in the repository (commit) func (c *RepoCache) NewBugWithFiles(title string, message string, files []git.Hash) (*BugCache, error) { - author, err := identity.GetUserIdentity(c.repo) + author, err := c.GetUserIdentity() if err != nil { return nil, err } @@ -405,8 +408,8 @@ func (c *RepoCache) NewBugWithFiles(title string, message string, files []git.Ha // NewBugWithFilesMeta create a new bug with attached files for the message, as // well as metadata for the Create operation. // The new bug is written in the repository (commit) -func (c *RepoCache) NewBugRaw(author identity.Interface, unixTime int64, title string, message string, files []git.Hash, metadata map[string]string) (*BugCache, error) { - b, op, err := bug.CreateWithFiles(author, unixTime, title, message, files) +func (c *RepoCache) NewBugRaw(author *IdentityCache, unixTime int64, title string, message string, files []git.Hash, metadata map[string]string) (*BugCache, error) { + b, op, err := bug.CreateWithFiles(author.Identity, unixTime, title, message, files) if err != nil { return nil, err } @@ -549,7 +552,7 @@ func repoIsAvailable(repo repository.Repo) error { } // ResolveIdentity retrieve an identity matching the exact given id -func (c *RepoCache) ResolveIdentity(id string) (*identity.Identity, error) { +func (c *RepoCache) ResolveIdentity(id string) (*IdentityCache, error) { cached, ok := c.identities[id] if ok { return cached, nil @@ -560,14 +563,15 @@ func (c *RepoCache) ResolveIdentity(id string) (*identity.Identity, error) { return nil, err } - c.identities[id] = i + cached = NewIdentityCache(c, i) + c.identities[id] = cached - return i, nil + return cached, nil } // ResolveIdentityPrefix retrieve an Identity matching an id prefix. // It fails if multiple identities match. -func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*identity.Identity, error) { +func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*IdentityCache, error) { // preallocate but empty matching := make([]string, 0, 5) @@ -590,7 +594,7 @@ func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*identity.Identity, er // ResolveIdentityImmutableMetadata retrieve an Identity that has the exact given metadata on // one of it's version. If multiple version have the same key, the first defined take precedence. -func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (*identity.Identity, error) { +func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (*IdentityCache, error) { // preallocate but empty matching := make([]string, 0, 5) @@ -611,19 +615,50 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) ( return c.ResolveIdentity(matching[0]) } +func (c *RepoCache) SetUserIdentity(i *IdentityCache) error { + err := identity.SetUserIdentity(c.repo, i.Identity) + if err != nil { + return err + } + + c.userIdentityId = i.Id() + + return nil +} + +func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) { + if c.userIdentityId != "" { + i, ok := c.identities[c.userIdentityId] + if ok { + return i, nil + } + } + + i, err := identity.GetUserIdentity(c.repo) + if err != nil { + return nil, err + } + + cached := NewIdentityCache(c, i) + c.identities[i.Id()] = cached + c.userIdentityId = i.Id() + + return cached, nil +} + // NewIdentity create a new identity // The new identity is written in the repository (commit) -func (c *RepoCache) NewIdentity(name string, email string) (*identity.Identity, error) { +func (c *RepoCache) NewIdentity(name string, email string) (*IdentityCache, error) { return c.NewIdentityRaw(name, email, "", "", 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) (*identity.Identity, error) { +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) NewIdentityRaw(name string, email string, login string, avatarUrl string, metadata map[string]string) (*identity.Identity, error) { +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) for key, value := range metadata { @@ -639,7 +674,8 @@ func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avat return nil, fmt.Errorf("identity %s already exist in the cache", i.Id()) } - c.identities[i.Id()] = i + cached := NewIdentityCache(c, i) + c.identities[i.Id()] = cached - return i, nil + return cached, nil } -- cgit From 947ea63522610bd16c32cf70812c129eda9bbb02 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Mon, 18 Feb 2019 14:11:37 +0100 Subject: identity: wip caching --- cache/bug_cache.go | 12 +++++-- cache/bug_excerpt.go | 20 ++++------- cache/identity_cache.go | 6 +++- cache/identity_excerpt.go | 48 ++++++++++++++++++++++++++ cache/repo_cache.go | 88 ++++++++++++++++++++++++++++++++++------------- 5 files changed, 133 insertions(+), 41 deletions(-) create mode 100644 cache/identity_excerpt.go (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index ce46837a..53a96275 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -228,9 +228,17 @@ func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target } func (c *BugCache) Commit() error { - return c.bug.Commit(c.repoCache.repo) + err := c.bug.Commit(c.repoCache.repo) + if err != nil { + return err + } + return c.notifyUpdated() } func (c *BugCache) CommitAsNeeded() error { - return c.bug.CommitAsNeeded(c.repoCache.repo) + err := c.bug.CommitAsNeeded(c.repoCache.repo) + if err != nil { + return err + } + return c.notifyUpdated() } diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index daf89c4f..d3645322 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -17,18 +17,13 @@ type BugExcerpt struct { CreateUnixTime int64 EditUnixTime int64 - Status bug.Status - Author AuthorExcerpt - Labels []bug.Label + Status bug.Status + AuthorId string + Labels []bug.Label CreateMetadata map[string]string } -type AuthorExcerpt struct { - Name string - Login string -} - func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { return &BugExcerpt{ Id: b.Id(), @@ -37,12 +32,9 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { CreateUnixTime: b.FirstOp().GetUnixTime(), EditUnixTime: snap.LastEditUnix(), Status: snap.Status, - Author: AuthorExcerpt{ - Login: snap.Author.Login(), - Name: snap.Author.Name(), - }, - Labels: snap.Labels, - CreateMetadata: b.FirstOp().AllMetadata(), + AuthorId: snap.Author.Id(), + Labels: snap.Labels, + CreateMetadata: b.FirstOp().AllMetadata(), } } diff --git a/cache/identity_cache.go b/cache/identity_cache.go index 93b2dc4b..c49e9519 100644 --- a/cache/identity_cache.go +++ b/cache/identity_cache.go @@ -4,7 +4,7 @@ import ( "github.com/MichaelMure/git-bug/identity" ) -// IdentityCache is a wrapper around an Identity. It provide multiple functions: +// IdentityCache is a wrapper around an Identity for caching. type IdentityCache struct { *identity.Identity repoCache *RepoCache @@ -17,6 +17,10 @@ func NewIdentityCache(repoCache *RepoCache, id *identity.Identity) *IdentityCach } } +func (i *IdentityCache) notifyUpdated() error { + return i.repoCache.identityUpdated(i.Identity.Id()) +} + func (i *IdentityCache) Commit() error { return i.Identity.Commit(i.repoCache.repo) } diff --git a/cache/identity_excerpt.go b/cache/identity_excerpt.go new file mode 100644 index 00000000..7bc660b6 --- /dev/null +++ b/cache/identity_excerpt.go @@ -0,0 +1,48 @@ +package cache + +import ( + "encoding/gob" + + "github.com/MichaelMure/git-bug/identity" +) + +// IdentityExcerpt hold a subset of the identity values to be able to sort and +// filter identities efficiently without having to read and compile each raw +// identity. +type IdentityExcerpt struct { + Id string + + Name string + Login string +} + +func NewIdentityExcerpt(i *identity.Identity) *IdentityExcerpt { + return &IdentityExcerpt{ + Id: i.Id(), + Name: i.Name(), + Login: i.Login(), + } +} + +// Package initialisation used to register the type for (de)serialization +func init() { + gob.Register(IdentityExcerpt{}) +} + +/* + * Sorting + */ + +type IdentityById []*IdentityExcerpt + +func (b IdentityById) Len() int { + return len(b) +} + +func (b IdentityById) Less(i, j int) bool { + return b[i].Id < b[j].Id +} + +func (b IdentityById) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} diff --git a/cache/repo_cache.go b/cache/repo_cache.go index f64a1b76..cec6f8b5 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -20,8 +20,12 @@ import ( "github.com/MichaelMure/git-bug/util/process" ) -const cacheFile = "cache" -const formatVersion = 1 +const bugCacheFile = "bug-cache" +const identityCacheFile = "identity-cache" + +// 1: original format +// 2: added cache for identities with a reference in the bug cache +const formatVersion = 2 // RepoCache is a cache for a Repository. This cache has multiple functions: // @@ -40,12 +44,17 @@ const formatVersion = 1 type RepoCache struct { // the underlying repo repo repository.ClockedRepo + // excerpt of bugs data for all bugs - excerpts map[string]*BugExcerpt + bugExcerpts map[string]*BugExcerpt // bug loaded in memory bugs map[string]*BugCache + + // excerpt of identities data for all identities + identitiesExcerpts map[string]*IdentityExcerpt // identities loaded in memory identities map[string]*IdentityCache + // the user identity's id, if known userIdentityId string } @@ -145,14 +154,27 @@ func (c *RepoCache) bugUpdated(id string) error { panic("missing bug in the cache") } - c.excerpts[id] = NewBugExcerpt(b.bug, b.Snapshot()) + c.bugExcerpts[id] = NewBugExcerpt(b.bug, b.Snapshot()) + + return c.write() +} + +// identityUpdated is a callback to trigger when the excerpt of an identity +// changed, that is each time an identity is updated +func (c *RepoCache) identityUpdated(id string) error { + i, ok := c.identities[id] + if !ok { + panic("missing identity in the cache") + } + + c.identitiesExcerpts[id] = NewIdentityExcerpt(i.Identity) return c.write() } // load will try to read from the disk the bug cache file func (c *RepoCache) load() error { - f, err := os.Open(cacheFilePath(c.repo)) + f, err := os.Open(bugCacheFilePath(c.repo)) if err != nil { return err } @@ -173,7 +195,7 @@ func (c *RepoCache) load() error { return fmt.Errorf("unknown cache format version %v", aux.Version) } - c.excerpts = aux.Excerpts + c.bugExcerpts = aux.Excerpts return nil } @@ -186,7 +208,7 @@ func (c *RepoCache) write() error { Excerpts map[string]*BugExcerpt }{ Version: formatVersion, - Excerpts: c.excerpts, + Excerpts: c.bugExcerpts, } encoder := gob.NewEncoder(&data) @@ -196,7 +218,7 @@ func (c *RepoCache) write() error { return err } - f, err := os.Create(cacheFilePath(c.repo)) + f, err := os.Create(bugCacheFilePath(c.repo)) if err != nil { return err } @@ -209,14 +231,18 @@ func (c *RepoCache) write() error { return f.Close() } -func cacheFilePath(repo repository.Repo) string { - return path.Join(repo.GetPath(), ".git", "git-bug", cacheFile) +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) } func (c *RepoCache) buildCache() error { _, _ = fmt.Fprintf(os.Stderr, "Building bug cache... ") - c.excerpts = make(map[string]*BugExcerpt) + c.bugExcerpts = make(map[string]*BugExcerpt) allBugs := bug.ReadAllLocalBugs(c.repo) @@ -226,7 +252,7 @@ func (c *RepoCache) buildCache() error { } snap := b.Bug.Compile() - c.excerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, &snap) + c.bugExcerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, &snap) } _, _ = fmt.Fprintln(os.Stderr, "Done.") @@ -257,7 +283,7 @@ func (c *RepoCache) ResolveBugPrefix(prefix string) (*BugCache, error) { // preallocate but empty matching := make([]string, 0, 5) - for id := range c.excerpts { + for id := range c.bugExcerpts { if strings.HasPrefix(id, prefix) { matching = append(matching, id) } @@ -281,7 +307,7 @@ func (c *RepoCache) ResolveBugCreateMetadata(key string, value string) (*BugCach // preallocate but empty matching := make([]string, 0, 5) - for id, excerpt := range c.excerpts { + for id, excerpt := range c.bugExcerpts { if excerpt.CreateMetadata[key] == value { matching = append(matching, id) } @@ -306,7 +332,7 @@ func (c *RepoCache) QueryBugs(query *Query) []string { var filtered []*BugExcerpt - for _, excerpt := range c.excerpts { + for _, excerpt := range c.bugExcerpts { if query.Match(excerpt) { filtered = append(filtered, excerpt) } @@ -342,10 +368,10 @@ func (c *RepoCache) QueryBugs(query *Query) []string { // AllBugsIds return all known bug ids func (c *RepoCache) AllBugsIds() []string { - result := make([]string, len(c.excerpts)) + result := make([]string, len(c.bugExcerpts)) i := 0 - for _, excerpt := range c.excerpts { + for _, excerpt := range c.bugExcerpts { result[i] = excerpt.Id i++ } @@ -353,11 +379,6 @@ func (c *RepoCache) AllBugsIds() []string { return result } -// ClearAllBugs clear all bugs kept in memory -func (c *RepoCache) ClearAllBugs() { - c.bugs = make(map[string]*BugCache) -} - // ValidLabels list valid labels // // Note: in the future, a proper label policy could be implemented where valid @@ -366,7 +387,7 @@ func (c *RepoCache) ClearAllBugs() { func (c *RepoCache) ValidLabels() []bug.Label { set := map[bug.Label]interface{}{} - for _, excerpt := range c.excerpts { + for _, excerpt := range c.bugExcerpts { for _, l := range excerpt.Labels { set[l] = nil } @@ -467,7 +488,7 @@ func (c *RepoCache) MergeAll(remote string) <-chan bug.MergeResult { case bug.MergeStatusNew, bug.MergeStatusUpdated: b := result.Bug snap := b.Compile() - c.excerpts[id] = NewBugExcerpt(b, &snap) + c.bugExcerpts[id] = NewBugExcerpt(b, &snap) } } @@ -615,6 +636,19 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) ( return c.ResolveIdentity(matching[0]) } +// AllIdentityIds return all known identity ids +func (c *RepoCache) AllIdentityIds() []string { + result := make([]string, len(c.identitiesExcerpts)) + + i := 0 + for _, excerpt := range c.identitiesExcerpts { + result[i] = excerpt.Id + i++ + } + + return result +} + func (c *RepoCache) SetUserIdentity(i *IdentityCache) error { err := identity.SetUserIdentity(c.repo, i.Identity) if err != nil { @@ -677,5 +711,11 @@ func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avat cached := NewIdentityCache(c, i) c.identities[i.Id()] = cached + // force the write of the excerpt + err = c.identityUpdated(i.Id()) + if err != nil { + return nil, err + } + return cached, nil } -- cgit From 54f9838f0ab22ce5285f21cdd117ad81c737d822 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Mon, 18 Feb 2019 23:16:47 +0100 Subject: identity: working identity cache --- cache/bug_excerpt.go | 35 +++++++++++-- cache/filter.go | 44 ++++++++++------ cache/identity_cache.go | 17 +++++- cache/repo_cache.go | 137 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 202 insertions(+), 31 deletions(-) (limited to 'cache') 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 -- cgit From 71f9290fdae7551f3d3ada2179ece4084304d734 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 19 Feb 2019 00:19:27 +0100 Subject: identity: store the times properly --- cache/repo_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cache') diff --git a/cache/repo_cache.go b/cache/repo_cache.go index d5768125..e87119fe 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -712,7 +712,7 @@ func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*IdentityCache, error) // preallocate but empty matching := make([]string, 0, 5) - for id := range c.identities { + for id := range c.identitiesExcerpts { if strings.HasPrefix(id, prefix) { matching = append(matching, id) } -- cgit From b8caddddc7aaf34b2da61c590fd1d9a0fae024fb Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 23 Feb 2019 13:01:46 +0100 Subject: identity: some UX cleanup --- cache/bug_excerpt.go | 10 +++++----- cache/filter.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'cache') diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index e39c8310..55518077 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -8,6 +8,11 @@ import ( "github.com/MichaelMure/git-bug/util/lamport" ) +// Package initialisation used to register the type for (de)serialization +func init() { + gob.Register(BugExcerpt{}) +} + // BugExcerpt hold a subset of the bug values to be able to sort and filter bugs // efficiently without having to read and compile each raw bugs. type BugExcerpt struct { @@ -63,11 +68,6 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { return e } -// Package initialisation used to register the type for (de)serialization -func init() { - gob.Register(BugExcerpt{}) -} - /* * Sorting */ diff --git a/cache/filter.go b/cache/filter.go index 3cbc132a..022a8ff2 100644 --- a/cache/filter.go +++ b/cache/filter.go @@ -6,7 +6,7 @@ import ( "github.com/MichaelMure/git-bug/bug" ) -// Filter is a functor that match a subset of bugs +// Filter is a predicate that match a subset of bugs type Filter func(repoCache *RepoCache, excerpt *BugExcerpt) bool // StatusFilter return a Filter that match a bug status -- cgit From e100ee9f10dd7f600b58bf3d24b36f9b286210d6 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 24 Feb 2019 12:58:04 +0100 Subject: github: fix 3 edge-case failures --- cache/bug_cache.go | 68 +++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'cache') diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 53a96275..5fc76658 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -57,8 +57,8 @@ func (e ErrMultipleMatchOp) Error() string { return fmt.Sprintf("Multiple matching operation found:\n%s", strings.Join(casted, "\n")) } -// ResolveTargetWithMetadata will find an operation that has the matching metadata -func (c *BugCache) ResolveTargetWithMetadata(key string, value string) (git.Hash, error) { +// ResolveOperationWithMetadata will find an operation that has the matching metadata +func (c *BugCache) ResolveOperationWithMetadata(key string, value string) (git.Hash, error) { // preallocate but empty matching := make([]git.Hash, 0, 5) @@ -86,45 +86,45 @@ func (c *BugCache) ResolveTargetWithMetadata(key string, value string) (git.Hash return matching[0], nil } -func (c *BugCache) AddComment(message string) error { +func (c *BugCache) AddComment(message string) (*bug.AddCommentOperation, error) { return c.AddCommentWithFiles(message, nil) } -func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error { +func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) (*bug.AddCommentOperation, error) { author, err := c.repoCache.GetUserIdentity() if err != nil { - return err + return nil, err } return c.AddCommentRaw(author, time.Now().Unix(), message, files, nil) } -func (c *BugCache) AddCommentRaw(author *IdentityCache, unixTime int64, message string, files []git.Hash, metadata map[string]string) error { +func (c *BugCache) AddCommentRaw(author *IdentityCache, unixTime int64, message string, files []git.Hash, metadata map[string]string) (*bug.AddCommentOperation, error) { op, err := bug.AddCommentWithFiles(c.bug, author.Identity, unixTime, message, files) if err != nil { - return err + return nil, err } for key, value := range metadata { op.SetMetadata(key, value) } - return c.notifyUpdated() + return op, c.notifyUpdated() } -func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelChangeResult, error) { +func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelChangeResult, *bug.LabelChangeOperation, error) { author, err := c.repoCache.GetUserIdentity() if err != nil { - return nil, err + return nil, nil, err } return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil) } -func (c *BugCache) ChangeLabelsRaw(author *IdentityCache, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) { +func (c *BugCache) ChangeLabelsRaw(author *IdentityCache, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, *bug.LabelChangeOperation, error) { changes, op, err := bug.ChangeLabels(c.bug, author.Identity, unixTime, added, removed) if err != nil { - return changes, err + return changes, nil, err } for key, value := range metadata { @@ -133,98 +133,98 @@ func (c *BugCache) ChangeLabelsRaw(author *IdentityCache, unixTime int64, added err = c.notifyUpdated() if err != nil { - return nil, err + return nil, nil, err } - return changes, nil + return changes, op, nil } -func (c *BugCache) Open() error { +func (c *BugCache) Open() (*bug.SetStatusOperation, error) { author, err := c.repoCache.GetUserIdentity() if err != nil { - return err + return nil, err } return c.OpenRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) OpenRaw(author *IdentityCache, unixTime int64, metadata map[string]string) error { +func (c *BugCache) OpenRaw(author *IdentityCache, unixTime int64, metadata map[string]string) (*bug.SetStatusOperation, error) { op, err := bug.Open(c.bug, author.Identity, unixTime) if err != nil { - return err + return nil, err } for key, value := range metadata { op.SetMetadata(key, value) } - return c.notifyUpdated() + return op, c.notifyUpdated() } -func (c *BugCache) Close() error { +func (c *BugCache) Close() (*bug.SetStatusOperation, error) { author, err := c.repoCache.GetUserIdentity() if err != nil { - return err + return nil, err } return c.CloseRaw(author, time.Now().Unix(), nil) } -func (c *BugCache) CloseRaw(author *IdentityCache, unixTime int64, metadata map[string]string) error { +func (c *BugCache) CloseRaw(author *IdentityCache, unixTime int64, metadata map[string]string) (*bug.SetStatusOperation, error) { op, err := bug.Close(c.bug, author.Identity, unixTime) if err != nil { - return err + return nil, err } for key, value := range metadata { op.SetMetadata(key, value) } - return c.notifyUpdated() + return op, c.notifyUpdated() } -func (c *BugCache) SetTitle(title string) error { +func (c *BugCache) SetTitle(title string) (*bug.SetTitleOperation, error) { author, err := c.repoCache.GetUserIdentity() if err != nil { - return err + return nil, err } return c.SetTitleRaw(author, time.Now().Unix(), title, nil) } -func (c *BugCache) SetTitleRaw(author *IdentityCache, unixTime int64, title string, metadata map[string]string) error { +func (c *BugCache) SetTitleRaw(author *IdentityCache, unixTime int64, title string, metadata map[string]string) (*bug.SetTitleOperation, error) { op, err := bug.SetTitle(c.bug, author.Identity, unixTime, title) if err != nil { - return err + return nil, err } for key, value := range metadata { op.SetMetadata(key, value) } - return c.notifyUpdated() + return op, c.notifyUpdated() } -func (c *BugCache) EditComment(target git.Hash, message string) error { +func (c *BugCache) EditComment(target git.Hash, message string) (*bug.EditCommentOperation, error) { author, err := c.repoCache.GetUserIdentity() if err != nil { - return err + return nil, err } return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil) } -func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target git.Hash, message string, metadata map[string]string) error { +func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target git.Hash, message string, metadata map[string]string) (*bug.EditCommentOperation, error) { op, err := bug.EditComment(c.bug, author.Identity, unixTime, target, message) if err != nil { - return err + return nil, err } for key, value := range metadata { op.SetMetadata(key, value) } - return c.notifyUpdated() + return op, c.notifyUpdated() } func (c *BugCache) Commit() error { -- cgit From 8bba6d1493fdf064ac9fede0a5098b1abe969052 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 24 Feb 2019 13:23:01 +0100 Subject: cache: fix ResolveIdentityImmutableMetadata byt storing metadata in IdentityExcerpt --- cache/identity_excerpt.go | 12 +++++++----- cache/repo_cache.go | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'cache') diff --git a/cache/identity_excerpt.go b/cache/identity_excerpt.go index 7bc660b6..0539a76b 100644 --- a/cache/identity_excerpt.go +++ b/cache/identity_excerpt.go @@ -12,15 +12,17 @@ import ( type IdentityExcerpt struct { Id string - Name string - Login string + Name string + Login string + ImmutableMetadata map[string]string } func NewIdentityExcerpt(i *identity.Identity) *IdentityExcerpt { return &IdentityExcerpt{ - Id: i.Id(), - Name: i.Name(), - Login: i.Login(), + Id: i.Id(), + Name: i.Name(), + Login: i.Login(), + ImmutableMetadata: i.ImmutableMetadata(), } } diff --git a/cache/repo_cache.go b/cache/repo_cache.go index e87119fe..78633a69 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -735,8 +735,8 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) ( // preallocate but empty matching := make([]string, 0, 5) - for id, i := range c.identities { - if i.ImmutableMetadata()[key] == value { + for id, i := range c.identitiesExcerpts { + if i.ImmutableMetadata[key] == value { matching = append(matching, id) } } -- cgit From 7a80d8f849861a6033cd0765e5d85a52b08a8854 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 24 Feb 2019 14:17:52 +0100 Subject: commands: add a super-fast "user ls" command --- cache/bug_excerpt.go | 4 ++++ cache/identity_excerpt.go | 26 +++++++++++++++++++++++--- cache/repo_cache.go | 12 ++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) (limited to 'cache') diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index 55518077..fd06e51b 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -68,6 +68,10 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { return e } +func (b *BugExcerpt) HumanId() string { + return bug.FormatHumanID(b.Id) +} + /* * Sorting */ diff --git a/cache/identity_excerpt.go b/cache/identity_excerpt.go index 0539a76b..2a13bc60 100644 --- a/cache/identity_excerpt.go +++ b/cache/identity_excerpt.go @@ -2,10 +2,16 @@ package cache import ( "encoding/gob" + "fmt" "github.com/MichaelMure/git-bug/identity" ) +// Package initialisation used to register the type for (de)serialization +func init() { + gob.Register(IdentityExcerpt{}) +} + // IdentityExcerpt hold a subset of the identity values to be able to sort and // filter identities efficiently without having to read and compile each raw // identity. @@ -26,9 +32,23 @@ func NewIdentityExcerpt(i *identity.Identity) *IdentityExcerpt { } } -// Package initialisation used to register the type for (de)serialization -func init() { - gob.Register(IdentityExcerpt{}) +func (i *IdentityExcerpt) HumanId() string { + return identity.FormatHumanID(i.Id) +} + +// DisplayName return a non-empty string to display, representing the +// identity, based on the non-empty values. +func (i *IdentityExcerpt) DisplayName() string { + switch { + case i.Name == "" && i.Login != "": + return i.Login + case i.Name != "" && i.Login == "": + return i.Name + case i.Name != "" && i.Login != "": + return fmt.Sprintf("%s (%s)", i.Name, i.Login) + } + + panic("invalid person data") } /* diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 78633a69..a50c745b 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -489,6 +489,12 @@ func (c *RepoCache) AllBugsIds() []string { return result } +// AllBugExcerpt return all known bug excerpt. +// This maps is read-only. +func (c *RepoCache) AllBugExcerpt() map[string]*BugExcerpt { + return c.bugExcerpts +} + // ValidLabels list valid labels // // Note: in the future, a proper label policy could be implemented where valid @@ -765,6 +771,12 @@ func (c *RepoCache) AllIdentityIds() []string { return result } +// AllIdentityExcerpt return all known identities excerpt. +// This maps is read-only. +func (c *RepoCache) AllIdentityExcerpt() map[string]*IdentityExcerpt { + return c.identitiesExcerpts +} + func (c *RepoCache) SetUserIdentity(i *IdentityCache) error { err := identity.SetUserIdentity(c.repo, i.Identity) if err != nil { -- cgit From 46beb4b886761ff69abe2ce45c2498a4fafe50d9 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 24 Feb 2019 18:49:12 +0100 Subject: identity: another round of cleanups --- cache/repo_cache.go | 4 ---- 1 file changed, 4 deletions(-) (limited to 'cache') diff --git a/cache/repo_cache.go b/cache/repo_cache.go index a50c745b..7423ee73 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -579,8 +579,6 @@ 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) } @@ -625,8 +623,6 @@ 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) } -- cgit From bad05a4f3d24eb092724f667ee33bb1956457dd3 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 26 Feb 2019 22:25:52 +0100 Subject: cache: better API to access excerpts --- cache/repo_cache.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'cache') diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 7423ee73..2b0fa360 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -387,6 +387,16 @@ func (c *RepoCache) ResolveBug(id string) (*BugCache, error) { return cached, nil } +// ResolveBugExcerpt retrieve a BugExcerpt matching the exact given id +func (c *RepoCache) ResolveBugExcerpt(id string) (*BugExcerpt, error) { + e, ok := c.bugExcerpts[id] + if !ok { + return nil, bug.ErrBugNotExist + } + + return e, nil +} + // ResolveBugPrefix retrieve a bug matching an id prefix. It fails if multiple // bugs match. func (c *RepoCache) ResolveBugPrefix(prefix string) (*BugCache, error) { @@ -489,12 +499,6 @@ func (c *RepoCache) AllBugsIds() []string { return result } -// AllBugExcerpt return all known bug excerpt. -// This maps is read-only. -func (c *RepoCache) AllBugExcerpt() map[string]*BugExcerpt { - return c.bugExcerpts -} - // ValidLabels list valid labels // // Note: in the future, a proper label policy could be implemented where valid @@ -708,6 +712,16 @@ func (c *RepoCache) ResolveIdentity(id string) (*IdentityCache, error) { return cached, nil } +// ResolveIdentityExcerpt retrieve a IdentityExcerpt matching the exact given id +func (c *RepoCache) ResolveIdentityExcerpt(id string) (*IdentityExcerpt, error) { + e, ok := c.identitiesExcerpts[id] + if !ok { + return nil, identity.ErrIdentityNotExist + } + + return e, nil +} + // ResolveIdentityPrefix retrieve an Identity matching an id prefix. // It fails if multiple identities match. func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*IdentityCache, error) { @@ -767,12 +781,6 @@ func (c *RepoCache) AllIdentityIds() []string { return result } -// AllIdentityExcerpt return all known identities excerpt. -// This maps is read-only. -func (c *RepoCache) AllIdentityExcerpt() map[string]*IdentityExcerpt { - return c.identitiesExcerpts -} - func (c *RepoCache) SetUserIdentity(i *IdentityCache) error { err := identity.SetUserIdentity(c.repo, i.Identity) if err != nil { -- cgit