aboutsummaryrefslogtreecommitdiffstats
path: root/cache
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2018-08-23 21:24:57 +0200
committerMichael Muré <batolettre@gmail.com>2018-08-23 21:24:57 +0200
commit0514edad1a6ee06902841e0c903fc2a2119b7e95 (patch)
tree104279a8939034ab163cac9bfc124166d001dda7 /cache
parente7648996c8f278d061fe03a5c4d255049da765e5 (diff)
downloadgit-bug-0514edad1a6ee06902841e0c903fc2a2119b7e95.tar.gz
cache: maintain, write and load from disk bug excerpts
Diffstat (limited to 'cache')
-rw-r--r--cache/bug_cache.go60
-rw-r--r--cache/bug_excerpt.go9
-rw-r--r--cache/cache.go45
-rw-r--r--cache/repo_cache.go133
4 files changed, 161 insertions, 86 deletions
diff --git a/cache/bug_cache.go b/cache/bug_cache.go
index f0fc7ff6..7df76efe 100644
--- a/cache/bug_cache.go
+++ b/cache/bug_cache.go
@@ -3,34 +3,18 @@ package cache
import (
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/bug/operations"
- "github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util"
)
-type BugCacher interface {
- Snapshot() *bug.Snapshot
-
- // Mutations
- AddComment(message string) error
- AddCommentWithFiles(message string, files []util.Hash) error
- ChangeLabels(added []string, removed []string) error
- Open() error
- Close() error
- SetTitle(title string) error
-
- Commit() error
- CommitAsNeeded() error
-}
-
type BugCache struct {
- repo repository.Repo
- bug *bug.WithSnapshot
+ repoCache *RepoCache
+ bug *bug.WithSnapshot
}
-func NewBugCache(repo repository.Repo, b *bug.Bug) BugCacher {
+func NewBugCache(repoCache *RepoCache, b *bug.Bug) *BugCache {
return &BugCache{
- repo: repo,
- bug: &bug.WithSnapshot{Bug: b},
+ repoCache: repoCache,
+ bug: &bug.WithSnapshot{Bug: b},
}
}
@@ -38,23 +22,31 @@ func (c *BugCache) Snapshot() *bug.Snapshot {
return c.bug.Snapshot()
}
+func (c *BugCache) notifyUpdated() error {
+ return c.repoCache.bugUpdated(c.bug.Id())
+}
+
func (c *BugCache) AddComment(message string) error {
- return c.AddCommentWithFiles(message, nil)
+ if err := c.AddCommentWithFiles(message, nil); err != nil {
+ return err
+ }
+
+ return c.notifyUpdated()
}
func (c *BugCache) AddCommentWithFiles(message string, files []util.Hash) error {
- author, err := bug.GetUser(c.repo)
+ author, err := bug.GetUser(c.repoCache.repo)
if err != nil {
return err
}
operations.CommentWithFiles(c.bug, author, message, files)
- return nil
+ return c.notifyUpdated()
}
func (c *BugCache) ChangeLabels(added []string, removed []string) error {
- author, err := bug.GetUser(c.repo)
+ author, err := bug.GetUser(c.repoCache.repo)
if err != nil {
return err
}
@@ -64,49 +56,49 @@ func (c *BugCache) ChangeLabels(added []string, removed []string) error {
return err
}
- return nil
+ return c.notifyUpdated()
}
func (c *BugCache) Open() error {
- author, err := bug.GetUser(c.repo)
+ author, err := bug.GetUser(c.repoCache.repo)
if err != nil {
return err
}
operations.Open(c.bug, author)
- return nil
+ return c.notifyUpdated()
}
func (c *BugCache) Close() error {
- author, err := bug.GetUser(c.repo)
+ author, err := bug.GetUser(c.repoCache.repo)
if err != nil {
return err
}
operations.Close(c.bug, author)
- return nil
+ return c.notifyUpdated()
}
func (c *BugCache) SetTitle(title string) error {
- author, err := bug.GetUser(c.repo)
+ author, err := bug.GetUser(c.repoCache.repo)
if err != nil {
return err
}
operations.SetTitle(c.bug, author, title)
- return nil
+ return c.notifyUpdated()
}
func (c *BugCache) Commit() error {
- return c.bug.Commit(c.repo)
+ return c.bug.Commit(c.repoCache.repo)
}
func (c *BugCache) CommitAsNeeded() error {
if c.bug.HasPendingOp() {
- return c.bug.Commit(c.repo)
+ return c.bug.Commit(c.repoCache.repo)
}
return nil
}
diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go
index 23ac459b..572f461a 100644
--- a/cache/bug_excerpt.go
+++ b/cache/bug_excerpt.go
@@ -1,6 +1,8 @@
package cache
import (
+ "encoding/gob"
+
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/util"
)
@@ -19,7 +21,7 @@ type BugExcerpt struct {
Author bug.Person
}
-func NewBugExcerpt(b *bug.Bug, snap bug.Snapshot) BugExcerpt {
+func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) BugExcerpt {
return BugExcerpt{
Id: b.Id(),
CreateLamportTime: b.CreateLamportTime(),
@@ -31,6 +33,11 @@ func NewBugExcerpt(b *bug.Bug, snap bug.Snapshot) BugExcerpt {
}
}
+// Package initialisation used to register the type for (de)serialization
+func init() {
+ gob.Register(BugExcerpt{})
+}
+
/*
* Sorting
*/
diff --git a/cache/cache.go b/cache/cache.go
index 618ec981..6dbafba1 100644
--- a/cache/cache.go
+++ b/cache/cache.go
@@ -13,29 +13,15 @@ import (
)
const lockfile = "lock"
-
-type Cacher interface {
- // RegisterRepository register a named repository. Use this for multi-repo setup
- RegisterRepository(ref string, repo repository.Repo) error
- // RegisterDefaultRepository register a unnamed repository. Use this for mono-repo setup
- RegisterDefaultRepository(repo repository.Repo) error
-
- // ResolveRepo retrieve a repository by name
- ResolveRepo(ref string) (RepoCacher, error)
- // DefaultRepo retrieve the default repository
- DefaultRepo() (RepoCacher, error)
-
- // Close will do anything that is needed to close the cache properly
- Close() error
-}
+const excerptsFile = "excerpts"
type RootCache struct {
- repos map[string]RepoCacher
+ repos map[string]*RepoCache
}
func NewCache() RootCache {
return RootCache{
- repos: make(map[string]RepoCacher),
+ repos: make(map[string]*RepoCache),
}
}
@@ -46,7 +32,12 @@ func (c *RootCache) RegisterRepository(ref string, repo repository.Repo) error {
return err
}
- c.repos[ref] = NewRepoCache(repo)
+ r, err := NewRepoCache(repo)
+ if err != nil {
+ return err
+ }
+
+ c.repos[ref] = r
return nil
}
@@ -57,7 +48,12 @@ func (c *RootCache) RegisterDefaultRepository(repo repository.Repo) error {
return err
}
- c.repos[""] = NewRepoCache(repo)
+ r, err := NewRepoCache(repo)
+ if err != nil {
+ return err
+ }
+
+ c.repos[""] = r
return nil
}
@@ -84,7 +80,7 @@ func (c *RootCache) lockRepository(repo repository.Repo) error {
}
// ResolveRepo retrieve a repository by name
-func (c *RootCache) DefaultRepo() (RepoCacher, error) {
+func (c *RootCache) DefaultRepo() (*RepoCache, error) {
if len(c.repos) != 1 {
return nil, fmt.Errorf("repository is not unique")
}
@@ -97,7 +93,7 @@ func (c *RootCache) DefaultRepo() (RepoCacher, error) {
}
// DefaultRepo retrieve the default repository
-func (c *RootCache) ResolveRepo(ref string) (RepoCacher, error) {
+func (c *RootCache) ResolveRepo(ref string) (*RepoCache, error) {
r, ok := c.repos[ref]
if !ok {
return nil, fmt.Errorf("unknown repo")
@@ -105,9 +101,10 @@ func (c *RootCache) ResolveRepo(ref string) (RepoCacher, error) {
return r, nil
}
+// Close will do anything that is needed to close the cache properly
func (c *RootCache) Close() error {
for _, cachedRepo := range c.repos {
- lockPath := repoLockFilePath(cachedRepo.Repository())
+ lockPath := repoLockFilePath(cachedRepo.repo)
err := os.Remove(lockPath)
if err != nil {
return err
@@ -116,6 +113,10 @@ func (c *RootCache) Close() error {
return nil
}
+// RepoIsAvailable check is the given repository is locked by a Cache.
+// Note: this is a smart function that will cleanup the lock file if the
+// corresponding process is not there anymore.
+// If no error is returned, the repo is free to edit.
func RepoIsAvailable(repo repository.Repo) error {
lockPath := repoLockFilePath(repo)
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index e58165d2..c73dbe9f 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -1,8 +1,12 @@
package cache
import (
+ "bytes"
+ "encoding/gob"
"fmt"
"io"
+ "os"
+ "path"
"strings"
"github.com/MichaelMure/git-bug/bug"
@@ -11,39 +15,110 @@ import (
"github.com/MichaelMure/git-bug/util"
)
-type RepoCacher interface {
- Repository() repository.Repo
- ResolveBug(id string) (BugCacher, error)
- ResolveBugPrefix(prefix string) (BugCacher, error)
- AllBugIds() ([]string, error)
- ClearAllBugs()
-
- // Mutations
- NewBug(title string, message string) (BugCacher, error)
- NewBugWithFiles(title string, message string, files []util.Hash) (BugCacher, error)
- Fetch(remote string) (string, error)
- MergeAll(remote string) <-chan bug.MergeResult
- Pull(remote string, out io.Writer) error
- Push(remote string) (string, error)
-}
-
type RepoCache struct {
- repo repository.Repo
- bugs map[string]BugCacher
+ repo repository.Repo
+ excerpts map[string]BugExcerpt
+ bugs map[string]*BugCache
}
-func NewRepoCache(r repository.Repo) RepoCacher {
- return &RepoCache{
+func NewRepoCache(r repository.Repo) (*RepoCache, error) {
+ c := &RepoCache{
repo: r,
- bugs: make(map[string]BugCacher),
+ bugs: make(map[string]*BugCache),
+ }
+
+ err := c.loadExcerpts()
+
+ if err == nil {
+ return c, nil
}
+
+ c.buildAllExcerpt()
+
+ return c, c.writeExcerpts()
}
func (c *RepoCache) Repository() repository.Repo {
return c.repo
}
-func (c *RepoCache) ResolveBug(id string) (BugCacher, error) {
+// bugUpdated is a callback to trigger when the excerpt of a bug changed,
+// that is each time a bug is updated
+func (c *RepoCache) bugUpdated(id string) error {
+ b, ok := c.bugs[id]
+ if !ok {
+ panic("missing bug in the cache")
+ }
+
+ c.excerpts[id] = NewBugExcerpt(b.bug, b.Snapshot())
+
+ return c.writeExcerpts()
+}
+
+// loadExcerpts will try to read from the disk the bug excerpt file
+func (c *RepoCache) loadExcerpts() error {
+ excerptsPath := repoExcerptsFilePath(c.repo)
+
+ f, err := os.Open(excerptsPath)
+ if err != nil {
+ return err
+ }
+
+ decoder := gob.NewDecoder(f)
+
+ var excerpts map[string]BugExcerpt
+
+ err = decoder.Decode(&excerpts)
+ if err != nil {
+ return err
+ }
+
+ c.excerpts = excerpts
+ return nil
+}
+
+// writeExcerpts will serialize on disk the BugExcerpt array
+func (c *RepoCache) writeExcerpts() error {
+ var data bytes.Buffer
+
+ encoder := gob.NewEncoder(&data)
+
+ err := encoder.Encode(c.excerpts)
+ if err != nil {
+ return err
+ }
+
+ excerptsPath := repoExcerptsFilePath(c.repo)
+
+ f, err := os.Create(excerptsPath)
+ if err != nil {
+ return err
+ }
+
+ _, err = f.Write(data.Bytes())
+ if err != nil {
+ return err
+ }
+
+ return f.Close()
+}
+
+func repoExcerptsFilePath(repo repository.Repo) string {
+ return path.Join(repo.GetPath(), ".git", "git-bug", excerptsFile)
+}
+
+func (c *RepoCache) buildAllExcerpt() {
+ c.excerpts = make(map[string]BugExcerpt)
+
+ allBugs := bug.ReadAllLocalBugs(c.repo)
+
+ for b := range allBugs {
+ snap := b.Bug.Compile()
+ c.excerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, &snap)
+ }
+}
+
+func (c *RepoCache) ResolveBug(id string) (*BugCache, error) {
cached, ok := c.bugs[id]
if ok {
return cached, nil
@@ -54,13 +129,13 @@ func (c *RepoCache) ResolveBug(id string) (BugCacher, error) {
return nil, err
}
- cached = NewBugCache(c.repo, b)
+ cached = NewBugCache(c, b)
c.bugs[id] = cached
return cached, nil
}
-func (c *RepoCache) ResolveBugPrefix(prefix string) (BugCacher, error) {
+func (c *RepoCache) ResolveBugPrefix(prefix string) (*BugCache, error) {
// preallocate but empty
matching := make([]string, 0, 5)
@@ -87,7 +162,7 @@ func (c *RepoCache) ResolveBugPrefix(prefix string) (BugCacher, error) {
return nil, err
}
- cached := NewBugCache(c.repo, b)
+ cached := NewBugCache(c, b)
c.bugs[b.Id()] = cached
return cached, nil
@@ -98,14 +173,14 @@ func (c *RepoCache) AllBugIds() ([]string, error) {
}
func (c *RepoCache) ClearAllBugs() {
- c.bugs = make(map[string]BugCacher)
+ c.bugs = make(map[string]*BugCache)
}
-func (c *RepoCache) NewBug(title string, message string) (BugCacher, error) {
+func (c *RepoCache) NewBug(title string, message string) (*BugCache, error) {
return c.NewBugWithFiles(title, message, nil)
}
-func (c *RepoCache) NewBugWithFiles(title string, message string, files []util.Hash) (BugCacher, error) {
+func (c *RepoCache) NewBugWithFiles(title string, message string, files []util.Hash) (*BugCache, error) {
author, err := bug.GetUser(c.repo)
if err != nil {
return nil, err
@@ -121,7 +196,7 @@ func (c *RepoCache) NewBugWithFiles(title string, message string, files []util.H
return nil, err
}
- cached := NewBugCache(c.repo, b)
+ cached := NewBugCache(c, b)
c.bugs[b.Id()] = cached
return cached, nil