diff options
-rw-r--r-- | cache/bug_excerpt.go | 35 | ||||
-rw-r--r-- | cache/filter.go | 44 | ||||
-rw-r--r-- | cache/identity_cache.go | 17 | ||||
-rw-r--r-- | cache/repo_cache.go | 137 | ||||
-rw-r--r-- | commands/user.go | 2 | ||||
-rw-r--r-- | identity/identity.go | 2 |
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] |