bug: fix an issue where Id would be used, then changed due to metadata

Michael Muré created

Change summary

bug/bug.go                             |   6 
bug/interface.go                       |  14 +-
bug/op_add_comment.go                  |  19 ++--
bug/op_create.go                       |  26 +++----
bug/op_edit_comment.go                 |  27 ++----
bug/op_label_change.go                 |  35 +++++----
bug/op_set_metadata.go                 |   2 
bug/op_set_status.go                   |  16 +++-
bug/op_set_title.go                    |  18 ++--
bug/operation_test.go                  |   2 
bug/with_snapshot.go                   |   9 +-
cache/bug_cache.go                     | 100 +++++----------------------
cache/repo_cache.go                    |   6 
cache/repo_cache_bug.go                |   6 -
cache/repo_cache_common.go             |   4 
misc/random_bugs/create_random_bugs.go |  11 +-
repository/gogit.go                    |   2 
repository/repo.go                     |   4 
18 files changed, 122 insertions(+), 185 deletions(-)

Detailed changes

bug/bug.go 🔗

@@ -140,14 +140,14 @@ func (bug *Bug) Operations() []Operation {
 }
 
 // Compile a bug in a easily usable snapshot
-func (bug *Bug) Compile() Snapshot {
-	snap := Snapshot{
+func (bug *Bug) Compile() *Snapshot {
+	snap := &Snapshot{
 		id:     bug.Id(),
 		Status: OpenStatus,
 	}
 
 	for _, op := range bug.Operations() {
-		op.Apply(&snap)
+		op.Apply(snap)
 		snap.Operations = append(snap.Operations, op)
 	}
 

bug/interface.go 🔗

@@ -7,22 +7,22 @@ import (
 )
 
 type Interface interface {
-	// Id return the Bug identifier
+	// Id returns the Bug identifier
 	Id() entity.Id
 
-	// Validate check if the Bug data is valid
+	// Validate checks if the Bug data is valid
 	Validate() error
 
 	// Append an operation into the staging area, to be committed later
 	Append(op Operation)
 
-	// Operations return the ordered operations
+	// Operations returns the ordered operations
 	Operations() []Operation
 
-	// NeedCommit indicate that the in-memory state changed and need to be commit in the repository
+	// NeedCommit indicates that the in-memory state changed and need to be commit in the repository
 	NeedCommit() bool
 
-	// Commit write the staging area in Git and move the operations to the packs
+	// Commit writes the staging area in Git and move the operations to the packs
 	Commit(repo repository.ClockedRepo) error
 
 	// FirstOp lookup for the very first operation of the bug.
@@ -33,8 +33,8 @@ type Interface interface {
 	// For a valid Bug, should never be nil
 	LastOp() Operation
 
-	// Compile a bug in a easily usable snapshot
-	Compile() Snapshot
+	// Compile a bug in an easily usable snapshot
+	Compile() *Snapshot
 
 	// CreateLamportTime return the Lamport time of creation
 	CreateLamportTime() lamport.Time

bug/op_add_comment.go 🔗

@@ -79,16 +79,15 @@ type AddCommentTimelineItem struct {
 // IsAuthored is a sign post method for gqlgen
 func (a *AddCommentTimelineItem) IsAuthored() {}
 
-// Convenience function to apply the operation
-func AddComment(b Interface, author identity.Interface, unixTime int64, message string) (*AddCommentOperation, error) {
-	return AddCommentWithFiles(b, author, unixTime, message, nil)
-}
-
-func AddCommentWithFiles(b Interface, author identity.Interface, unixTime int64, message string, files []repository.Hash) (*AddCommentOperation, error) {
-	addCommentOp := NewAddCommentOp(author, unixTime, message, files)
-	if err := addCommentOp.Validate(); err != nil {
+// AddComment is a convenience function to add a comment to a bug
+func AddComment(b Interface, author identity.Interface, unixTime int64, message string, files []repository.Hash, metadata map[string]string) (*AddCommentOperation, error) {
+	op := NewAddCommentOp(author, unixTime, message, files)
+	for key, val := range metadata {
+		op.SetMetadata(key, val)
+	}
+	if err := op.Validate(); err != nil {
 		return nil, err
 	}
-	b.Append(addCommentOp)
-	return addCommentOp, nil
+	b.Append(op)
+	return op, nil
 }

bug/op_create.go 🔗

@@ -97,20 +97,16 @@ type CreateTimelineItem struct {
 // IsAuthored is a sign post method for gqlgen
 func (c *CreateTimelineItem) IsAuthored() {}
 
-// Convenience function to apply the operation
-func Create(author identity.Interface, unixTime int64, title, message string) (*Bug, *CreateOperation, error) {
-	return CreateWithFiles(author, unixTime, title, message, nil)
-}
-
-func CreateWithFiles(author identity.Interface, unixTime int64, title, message string, files []repository.Hash) (*Bug, *CreateOperation, error) {
-	newBug := NewBug()
-	createOp := NewCreateOp(author, unixTime, title, message, files)
-
-	if err := createOp.Validate(); err != nil {
-		return nil, createOp, err
+// Create is a convenience function to create a bug
+func Create(author identity.Interface, unixTime int64, title, message string, files []repository.Hash, metadata map[string]string) (*Bug, *CreateOperation, error) {
+	b := NewBug()
+	op := NewCreateOp(author, unixTime, title, message, files)
+	for key, val := range metadata {
+		op.SetMetadata(key, val)
 	}
-
-	newBug.Append(createOp)
-
-	return newBug, createOp, nil
+	if err := op.Validate(); err != nil {
+		return nil, op, err
+	}
+	b.Append(op)
+	return b, op, nil
 }

bug/op_edit_comment.go 🔗

@@ -110,27 +110,20 @@ func NewEditCommentOp(author identity.Interface, unixTime int64, target entity.I
 }
 
 // EditComment is a convenience function to apply the operation
-func EditComment(b Interface, author identity.Interface, unixTime int64, target entity.Id, message string) (*EditCommentOperation, error) {
-	return EditCommentWithFiles(b, author, unixTime, target, message, nil)
-}
-
-func EditCommentWithFiles(b Interface, author identity.Interface, unixTime int64, target entity.Id, message string, files []repository.Hash) (*EditCommentOperation, error) {
-	editCommentOp := NewEditCommentOp(author, unixTime, target, message, files)
-	if err := editCommentOp.Validate(); err != nil {
+func EditComment(b Interface, author identity.Interface, unixTime int64, target entity.Id, message string, files []repository.Hash, metadata map[string]string) (*EditCommentOperation, error) {
+	op := NewEditCommentOp(author, unixTime, target, message, files)
+	for key, val := range metadata {
+		op.SetMetadata(key, val)
+	}
+	if err := op.Validate(); err != nil {
 		return nil, err
 	}
-	b.Append(editCommentOp)
-	return editCommentOp, nil
+	b.Append(op)
+	return op, nil
 }
 
 // EditCreateComment is a convenience function to edit the body of a bug (the first comment)
-func EditCreateComment(b Interface, author identity.Interface, unixTime int64, message string) (*EditCommentOperation, error) {
-	createOp := b.FirstOp().(*CreateOperation)
-	return EditComment(b, author, unixTime, createOp.Id(), message)
-}
-
-// EditCreateCommentWithFiles is a convenience function to edit the body of a bug (the first comment)
-func EditCreateCommentWithFiles(b Interface, author identity.Interface, unixTime int64, message string, files []repository.Hash) (*EditCommentOperation, error) {
+func EditCreateComment(b Interface, author identity.Interface, unixTime int64, message string, files []repository.Hash, metadata map[string]string) (*EditCommentOperation, error) {
 	createOp := b.FirstOp().(*CreateOperation)
-	return EditCommentWithFiles(b, author, unixTime, createOp.Id(), message, files)
+	return EditComment(b, author, unixTime, createOp.Id(), message, files, metadata)
 }

bug/op_label_change.go 🔗

@@ -25,7 +25,7 @@ func (op *LabelChangeOperation) Id() entity.Id {
 	return dag.IdOperation(op, &op.OpBase)
 }
 
-// Apply apply the operation
+// Apply applies the operation
 func (op *LabelChangeOperation) Apply(snapshot *Snapshot) {
 	snapshot.addActor(op.Author())
 
@@ -113,10 +113,10 @@ func (l LabelChangeTimelineItem) Id() entity.Id {
 }
 
 // IsAuthored is a sign post method for gqlgen
-func (l *LabelChangeTimelineItem) IsAuthored() {}
+func (l LabelChangeTimelineItem) IsAuthored() {}
 
-// ChangeLabels is a convenience function to apply the operation
-func ChangeLabels(b Interface, author identity.Interface, unixTime int64, add, remove []string) ([]LabelChangeResult, *LabelChangeOperation, error) {
+// ChangeLabels is a convenience function to change labels on a bug
+func ChangeLabels(b Interface, author identity.Interface, unixTime int64, add, remove []string, metadata map[string]string) ([]LabelChangeResult, *LabelChangeOperation, error) {
 	var added, removed []Label
 	var results []LabelChangeResult
 
@@ -164,23 +164,25 @@ func ChangeLabels(b Interface, author identity.Interface, unixTime int64, add, r
 		return results, nil, fmt.Errorf("no label added or removed")
 	}
 
-	labelOp := NewLabelChangeOperation(author, unixTime, added, removed)
-
-	if err := labelOp.Validate(); err != nil {
+	op := NewLabelChangeOperation(author, unixTime, added, removed)
+	for key, val := range metadata {
+		op.SetMetadata(key, val)
+	}
+	if err := op.Validate(); err != nil {
 		return nil, nil, err
 	}
 
-	b.Append(labelOp)
+	b.Append(op)
 
-	return results, labelOp, nil
+	return results, op, nil
 }
 
 // ForceChangeLabels is a convenience function to apply the operation
 // The difference with ChangeLabels is that no checks of deduplications are done. You are entirely
-// responsible of what you are doing. In the general case, you want to use ChangeLabels instead.
+// responsible for what you are doing. In the general case, you want to use ChangeLabels instead.
 // The intended use of this function is to allow importers to create legal but unexpected label changes,
 // like removing a label with no information of when it was added before.
-func ForceChangeLabels(b Interface, author identity.Interface, unixTime int64, add, remove []string) (*LabelChangeOperation, error) {
+func ForceChangeLabels(b Interface, author identity.Interface, unixTime int64, add, remove []string, metadata map[string]string) (*LabelChangeOperation, error) {
 	added := make([]Label, len(add))
 	for i, str := range add {
 		added[i] = Label(str)
@@ -191,15 +193,18 @@ func ForceChangeLabels(b Interface, author identity.Interface, unixTime int64, a
 		removed[i] = Label(str)
 	}
 
-	labelOp := NewLabelChangeOperation(author, unixTime, added, removed)
+	op := NewLabelChangeOperation(author, unixTime, added, removed)
 
-	if err := labelOp.Validate(); err != nil {
+	for key, val := range metadata {
+		op.SetMetadata(key, val)
+	}
+	if err := op.Validate(); err != nil {
 		return nil, err
 	}
 
-	b.Append(labelOp)
+	b.Append(op)
 
-	return labelOp, nil
+	return op, nil
 }
 
 func labelExist(labels []Label, label Label) bool {

bug/op_set_metadata.go 🔗

@@ -10,7 +10,7 @@ func NewSetMetadataOp(author identity.Interface, unixTime int64, target entity.I
 	return dag.NewSetMetadataOp[*Snapshot](SetMetadataOp, author, unixTime, target, newMetadata)
 }
 
-// Convenience function to apply the operation
+// SetMetadata is a convenience function to add metadata on another operation
 func SetMetadata(b Interface, author identity.Interface, unixTime int64, target entity.Id, newMetadata map[string]string) (*dag.SetMetadataOperation[*Snapshot], error) {
 	op := NewSetMetadataOp(author, unixTime, target, newMetadata)
 	if err := op.Validate(); err != nil {

bug/op_set_status.go 🔗

@@ -66,11 +66,14 @@ func (s SetStatusTimelineItem) Id() entity.Id {
 }
 
 // IsAuthored is a sign post method for gqlgen
-func (s *SetStatusTimelineItem) IsAuthored() {}
+func (s SetStatusTimelineItem) IsAuthored() {}
 
-// Convenience function to apply the operation
-func Open(b Interface, author identity.Interface, unixTime int64) (*SetStatusOperation, error) {
+// Open is a convenience function to change a bugs state to Open
+func Open(b Interface, author identity.Interface, unixTime int64, metadata map[string]string) (*SetStatusOperation, error) {
 	op := NewSetStatusOp(author, unixTime, OpenStatus)
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
 	if err := op.Validate(); err != nil {
 		return nil, err
 	}
@@ -78,9 +81,12 @@ func Open(b Interface, author identity.Interface, unixTime int64) (*SetStatusOpe
 	return op, nil
 }
 
-// Convenience function to apply the operation
-func Close(b Interface, author identity.Interface, unixTime int64) (*SetStatusOperation, error) {
+// Close is a convenience function to change a bugs state to Close
+func Close(b Interface, author identity.Interface, unixTime int64, metadata map[string]string) (*SetStatusOperation, error) {
 	op := NewSetStatusOp(author, unixTime, ClosedStatus)
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
 	if err := op.Validate(); err != nil {
 		return nil, err
 	}

bug/op_set_title.go 🔗

@@ -80,10 +80,10 @@ func (s SetTitleTimelineItem) Id() entity.Id {
 }
 
 // IsAuthored is a sign post method for gqlgen
-func (s *SetTitleTimelineItem) IsAuthored() {}
+func (s SetTitleTimelineItem) IsAuthored() {}
 
-// Convenience function to apply the operation
-func SetTitle(b Interface, author identity.Interface, unixTime int64, title string) (*SetTitleOperation, error) {
+// SetTitle is a convenience function to change a bugs title
+func SetTitle(b Interface, author identity.Interface, unixTime int64, title string, metadata map[string]string) (*SetTitleOperation, error) {
 	var lastTitleOp *SetTitleOperation
 	for _, op := range b.Operations() {
 		switch op := op.(type) {
@@ -99,12 +99,14 @@ func SetTitle(b Interface, author identity.Interface, unixTime int64, title stri
 		was = b.FirstOp().(*CreateOperation).Title
 	}
 
-	setTitleOp := NewSetTitleOp(author, unixTime, title, was)
-
-	if err := setTitleOp.Validate(); err != nil {
+	op := NewSetTitleOp(author, unixTime, title, was)
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
+	if err := op.Validate(); err != nil {
 		return nil, err
 	}
 
-	b.Append(setTitleOp)
-	return setTitleOp, nil
+	b.Append(op)
+	return op, nil
 }

bug/operation_test.go 🔗

@@ -104,7 +104,7 @@ func TestID(t *testing.T) {
 		err = rene.Commit(repo)
 		require.NoError(t, err)
 
-		b, op, err := Create(rene, time.Now().Unix(), "title", "message")
+		b, op, err := Create(rene, time.Now().Unix(), "title", "message", nil, nil)
 		require.NoError(t, err)
 
 		id1 := op.Id()

bug/with_snapshot.go 🔗

@@ -1,6 +1,8 @@
 package bug
 
-import "github.com/MichaelMure/git-bug/repository"
+import (
+	"github.com/MichaelMure/git-bug/repository"
+)
 
 var _ Interface = &WithSnapshot{}
 
@@ -10,11 +12,10 @@ type WithSnapshot struct {
 	snap *Snapshot
 }
 
-// Snapshot return the current snapshot
-func (b *WithSnapshot) Snapshot() *Snapshot {
+func (b *WithSnapshot) Compile() *Snapshot {
 	if b.snap == nil {
 		snap := b.Bug.Compile()
-		b.snap = &snap
+		b.snap = snap
 	}
 	return b.snap
 }

cache/bug_cache.go 🔗

@@ -34,7 +34,7 @@ func NewBugCache(repoCache *RepoCache, b *bug.Bug) *BugCache {
 func (c *BugCache) Snapshot() *bug.Snapshot {
 	c.mu.RLock()
 	defer c.mu.RUnlock()
-	return c.bug.Snapshot()
+	return c.bug.Compile()
 }
 
 func (c *BugCache) Id() entity.Id {
@@ -85,18 +85,11 @@ func (c *BugCache) AddCommentWithFiles(message string, files []repository.Hash)
 
 func (c *BugCache) AddCommentRaw(author *IdentityCache, unixTime int64, message string, files []repository.Hash, metadata map[string]string) (*bug.AddCommentOperation, error) {
 	c.mu.Lock()
-	op, err := bug.AddCommentWithFiles(c.bug, author.Identity, unixTime, message, files)
+	op, err := bug.AddComment(c.bug, author, unixTime, message, files, metadata)
+	c.mu.Unlock()
 	if err != nil {
-		c.mu.Unlock()
 		return nil, err
 	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
-	c.mu.Unlock()
-
 	return op, c.notifyUpdated()
 }
 
@@ -111,24 +104,12 @@ func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelCh
 
 func (c *BugCache) ChangeLabelsRaw(author *IdentityCache, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, *bug.LabelChangeOperation, error) {
 	c.mu.Lock()
-	changes, op, err := bug.ChangeLabels(c.bug, author.Identity, unixTime, added, removed)
-	if err != nil {
-		c.mu.Unlock()
-		return changes, nil, err
-	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
+	changes, op, err := bug.ChangeLabels(c.bug, author.Identity, unixTime, added, removed, metadata)
 	c.mu.Unlock()
-
-	err = c.notifyUpdated()
 	if err != nil {
-		return nil, nil, err
+		return changes, nil, err
 	}
-
-	return changes, op, nil
+	return changes, op, c.notifyUpdated()
 }
 
 func (c *BugCache) ForceChangeLabels(added []string, removed []string) (*bug.LabelChangeOperation, error) {
@@ -142,23 +123,12 @@ func (c *BugCache) ForceChangeLabels(added []string, removed []string) (*bug.Lab
 
 func (c *BugCache) ForceChangeLabelsRaw(author *IdentityCache, unixTime int64, added []string, removed []string, metadata map[string]string) (*bug.LabelChangeOperation, error) {
 	c.mu.Lock()
-	op, err := bug.ForceChangeLabels(c.bug, author.Identity, unixTime, added, removed)
-	if err != nil {
-		c.mu.Unlock()
-		return nil, err
-	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
+	op, err := bug.ForceChangeLabels(c.bug, author.Identity, unixTime, added, removed, metadata)
 	c.mu.Unlock()
-	err = c.notifyUpdated()
 	if err != nil {
 		return nil, err
 	}
-
-	return op, nil
+	return op, c.notifyUpdated()
 }
 
 func (c *BugCache) Open() (*bug.SetStatusOperation, error) {
@@ -172,17 +142,11 @@ func (c *BugCache) Open() (*bug.SetStatusOperation, error) {
 
 func (c *BugCache) OpenRaw(author *IdentityCache, unixTime int64, metadata map[string]string) (*bug.SetStatusOperation, error) {
 	c.mu.Lock()
-	op, err := bug.Open(c.bug, author.Identity, unixTime)
+	op, err := bug.Open(c.bug, author.Identity, unixTime, metadata)
+	c.mu.Unlock()
 	if err != nil {
-		c.mu.Unlock()
 		return nil, err
 	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
-	c.mu.Unlock()
 	return op, c.notifyUpdated()
 }
 
@@ -197,17 +161,11 @@ func (c *BugCache) Close() (*bug.SetStatusOperation, error) {
 
 func (c *BugCache) CloseRaw(author *IdentityCache, unixTime int64, metadata map[string]string) (*bug.SetStatusOperation, error) {
 	c.mu.Lock()
-	op, err := bug.Close(c.bug, author.Identity, unixTime)
+	op, err := bug.Close(c.bug, author.Identity, unixTime, metadata)
+	c.mu.Unlock()
 	if err != nil {
-		c.mu.Unlock()
 		return nil, err
 	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
-	c.mu.Unlock()
 	return op, c.notifyUpdated()
 }
 
@@ -222,17 +180,11 @@ func (c *BugCache) SetTitle(title string) (*bug.SetTitleOperation, error) {
 
 func (c *BugCache) SetTitleRaw(author *IdentityCache, unixTime int64, title string, metadata map[string]string) (*bug.SetTitleOperation, error) {
 	c.mu.Lock()
-	op, err := bug.SetTitle(c.bug, author.Identity, unixTime, title)
+	op, err := bug.SetTitle(c.bug, author.Identity, unixTime, title, metadata)
+	c.mu.Unlock()
 	if err != nil {
-		c.mu.Unlock()
 		return nil, err
 	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
-	c.mu.Unlock()
 	return op, c.notifyUpdated()
 }
 
@@ -249,17 +201,11 @@ func (c *BugCache) EditCreateComment(body string) (*bug.EditCommentOperation, er
 // EditCreateCommentRaw is a convenience function to edit the body of a bug (the first comment)
 func (c *BugCache) EditCreateCommentRaw(author *IdentityCache, unixTime int64, body string, metadata map[string]string) (*bug.EditCommentOperation, error) {
 	c.mu.Lock()
-	op, err := bug.EditCreateComment(c.bug, author.Identity, unixTime, body)
+	op, err := bug.EditCreateComment(c.bug, author.Identity, unixTime, body, nil, metadata)
+	c.mu.Unlock()
 	if err != nil {
-		c.mu.Unlock()
 		return nil, err
 	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
-	c.mu.Unlock()
 	return op, c.notifyUpdated()
 }
 
@@ -274,17 +220,11 @@ func (c *BugCache) EditComment(target entity.Id, message string) (*bug.EditComme
 
 func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target entity.Id, message string, metadata map[string]string) (*bug.EditCommentOperation, error) {
 	c.mu.Lock()
-	op, err := bug.EditComment(c.bug, author.Identity, unixTime, target, message)
+	op, err := bug.EditComment(c.bug, author.Identity, unixTime, target, message, nil, metadata)
+	c.mu.Unlock()
 	if err != nil {
-		c.mu.Unlock()
 		return nil, err
 	}
-
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
-	c.mu.Unlock()
 	return op, c.notifyUpdated()
 }
 
@@ -300,12 +240,10 @@ func (c *BugCache) SetMetadata(target entity.Id, newMetadata map[string]string)
 func (c *BugCache) SetMetadataRaw(author *IdentityCache, unixTime int64, target entity.Id, newMetadata map[string]string) (*dag.SetMetadataOperation[*bug.Snapshot], error) {
 	c.mu.Lock()
 	op, err := bug.SetMetadata(c.bug, author.Identity, unixTime, target, newMetadata)
+	c.mu.Unlock()
 	if err != nil {
-		c.mu.Unlock()
 		return nil, err
 	}
-
-	c.mu.Unlock()
 	return op, c.notifyUpdated()
 }
 

cache/repo_cache.go 🔗

@@ -209,9 +209,9 @@ func (c *RepoCache) buildCache() error {
 		}
 
 		snap := b.Bug.Compile()
-		c.bugExcerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, &snap)
+		c.bugExcerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, snap)
 
-		if err := c.addBugToSearchIndex(&snap); err != nil {
+		if err := c.addBugToSearchIndex(snap); err != nil {
 			return err
 		}
 	}
@@ -222,7 +222,7 @@ func (c *RepoCache) buildCache() error {
 }
 
 // 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
+// Note: this is a smart function that will clean 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.RepoStorage) error {

cache/repo_cache_bug.go 🔗

@@ -461,15 +461,11 @@ func (c *RepoCache) NewBugWithFiles(title string, message string, files []reposi
 // well as metadata for the Create operation.
 // The new bug is written in the repository (commit)
 func (c *RepoCache) NewBugRaw(author *IdentityCache, unixTime int64, title string, message string, files []repository.Hash, metadata map[string]string) (*BugCache, *bug.CreateOperation, error) {
-	b, op, err := bug.CreateWithFiles(author.Identity, unixTime, title, message, files)
+	b, op, err := bug.Create(author.Identity, unixTime, title, message, files, metadata)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	for key, value := range metadata {
-		op.SetMetadata(key, value)
-	}
-
 	err = b.Commit(c.repo)
 	if err != nil {
 		return nil, nil, err

cache/repo_cache_common.go 🔗

@@ -36,7 +36,7 @@ func (c *RepoCache) Keyring() repository.Keyring {
 	return c.repo.Keyring()
 }
 
-// GetUserName returns the name the the user has used to configure git
+// GetUserName returns the name the user has used to configure git
 func (c *RepoCache) GetUserName() (string, error) {
 	return c.repo.GetUserName()
 }
@@ -131,7 +131,7 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
 				b := result.Entity.(*bug.Bug)
 				snap := b.Compile()
 				c.muBug.Lock()
-				c.bugExcerpts[result.Id] = NewBugExcerpt(b, &snap)
+				c.bugExcerpts[result.Id] = NewBugExcerpt(b, snap)
 				c.muBug.Unlock()
 			}
 		}

misc/random_bugs/create_random_bugs.go 🔗

@@ -87,6 +87,7 @@ func generateRandomBugsWithSeed(opts Options, seed int64) []*bug.Bug {
 			time.Now().Unix(),
 			fake.Sentence(),
 			paragraphs(),
+			nil, nil,
 		)
 
 		if err != nil {
@@ -143,19 +144,19 @@ func paragraphs() string {
 }
 
 func comment(b bug.Interface, p identity.Interface, timestamp int64) {
-	_, _ = bug.AddComment(b, p, timestamp, paragraphs())
+	_, _ = bug.AddComment(b, p, timestamp, paragraphs(), nil, nil)
 }
 
 func title(b bug.Interface, p identity.Interface, timestamp int64) {
-	_, _ = bug.SetTitle(b, p, timestamp, fake.Sentence())
+	_, _ = bug.SetTitle(b, p, timestamp, fake.Sentence(), nil)
 }
 
 func open(b bug.Interface, p identity.Interface, timestamp int64) {
-	_, _ = bug.Open(b, p, timestamp)
+	_, _ = bug.Open(b, p, timestamp, nil)
 }
 
 func close(b bug.Interface, p identity.Interface, timestamp int64) {
-	_, _ = bug.Close(b, p, timestamp)
+	_, _ = bug.Close(b, p, timestamp, nil)
 }
 
 var addedLabels []string
@@ -182,5 +183,5 @@ func labels(b bug.Interface, p identity.Interface, timestamp int64) {
 	// ignore error
 	// if the randomisation produce no changes, no op
 	// is added to the bug
-	_, _, _ = bug.ChangeLabels(b, p, timestamp, added, removed)
+	_, _, _ = bug.ChangeLabels(b, p, timestamp, added, removed, nil)
 }

repository/gogit.go 🔗

@@ -235,7 +235,7 @@ func (repo *GoGitRepo) Keyring() Keyring {
 	return repo.keyring
 }
 
-// GetUserName returns the name the the user has used to configure git
+// GetUserName returns the name the user has used to configure git
 func (repo *GoGitRepo) GetUserName() (string, error) {
 	return repo.AnyConfig().ReadString("user.name")
 }

repository/repo.go 🔗

@@ -60,9 +60,9 @@ type RepoKeyring interface {
 	Keyring() Keyring
 }
 
-// RepoCommon represent the common function the we want all the repo to implement
+// RepoCommon represent the common function we want all repos to implement
 type RepoCommon interface {
-	// GetUserName returns the name the the user has used to configure git
+	// GetUserName returns the name the user has used to configure git
 	GetUserName() (string, error)
 
 	// GetUserEmail returns the email address that the user has used to configure git.