Detailed changes
  
  
    
    @@ -70,7 +70,7 @@ func TestPushPull(t *testing.T) {
 	repoA, repoB, remote := setupRepos(t)
 	defer cleanupRepos(repoA, repoB, remote)
 
-	bug1, err := Create(rene, unix, "bug1", "message")
+	bug1, _, err := Create(rene, unix, "bug1", "message")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
@@ -89,7 +89,7 @@ func TestPushPull(t *testing.T) {
 	}
 
 	// B --> remote --> A
-	bug2, err := Create(rene, unix, "bug2", "message")
+	bug2, _, err := Create(rene, unix, "bug2", "message")
 	assert.Nil(t, err)
 	err = bug2.Commit(repoB)
 	assert.Nil(t, err)
@@ -132,7 +132,7 @@ func _RebaseTheirs(t testing.TB) {
 	repoA, repoB, remote := setupRepos(t)
 	defer cleanupRepos(repoA, repoB, remote)
 
-	bug1, err := Create(rene, unix, "bug1", "message")
+	bug1, _, err := Create(rene, unix, "bug1", "message")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
@@ -148,11 +148,11 @@ func _RebaseTheirs(t testing.TB) {
 	bug2, err := ReadLocalBug(repoB, bug1.Id())
 	assert.Nil(t, err)
 
-	err = AddComment(bug2, rene, unix, "message2")
+	_, err = AddComment(bug2, rene, unix, "message2")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message3")
+	_, err = AddComment(bug2, rene, unix, "message3")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message4")
+	_, err = AddComment(bug2, rene, unix, "message4")
 	assert.Nil(t, err)
 	err = bug2.Commit(repoB)
 	assert.Nil(t, err)
@@ -193,7 +193,7 @@ func _RebaseOurs(t testing.TB) {
 	repoA, repoB, remote := setupRepos(t)
 	defer cleanupRepos(repoA, repoB, remote)
 
-	bug1, err := Create(rene, unix, "bug1", "message")
+	bug1, _, err := Create(rene, unix, "bug1", "message")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
@@ -206,29 +206,29 @@ func _RebaseOurs(t testing.TB) {
 	err = Pull(repoB, "origin")
 	assert.Nil(t, err)
 
-	err = AddComment(bug1, rene, unix, "message2")
+	_, err = AddComment(bug1, rene, unix, "message2")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message3")
+	_, err = AddComment(bug1, rene, unix, "message3")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message4")
+	_, err = AddComment(bug1, rene, unix, "message4")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
 
-	err = AddComment(bug1, rene, unix, "message5")
+	_, err = AddComment(bug1, rene, unix, "message5")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message6")
+	_, err = AddComment(bug1, rene, unix, "message6")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message7")
+	_, err = AddComment(bug1, rene, unix, "message7")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
 
-	err = AddComment(bug1, rene, unix, "message8")
+	_, err = AddComment(bug1, rene, unix, "message8")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message9")
+	_, err = AddComment(bug1, rene, unix, "message9")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message10")
+	_, err = AddComment(bug1, rene, unix, "message10")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
@@ -274,7 +274,7 @@ func _RebaseConflict(t testing.TB) {
 	repoA, repoB, remote := setupRepos(t)
 	defer cleanupRepos(repoA, repoB, remote)
 
-	bug1, err := Create(rene, unix, "bug1", "message")
+	bug1, _, err := Create(rene, unix, "bug1", "message")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
@@ -287,29 +287,29 @@ func _RebaseConflict(t testing.TB) {
 	err = Pull(repoB, "origin")
 	assert.Nil(t, err)
 
-	err = AddComment(bug1, rene, unix, "message2")
+	_, err = AddComment(bug1, rene, unix, "message2")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message3")
+	_, err = AddComment(bug1, rene, unix, "message3")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message4")
+	_, err = AddComment(bug1, rene, unix, "message4")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
 
-	err = AddComment(bug1, rene, unix, "message5")
+	_, err = AddComment(bug1, rene, unix, "message5")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message6")
+	_, err = AddComment(bug1, rene, unix, "message6")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message7")
+	_, err = AddComment(bug1, rene, unix, "message7")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
 
-	err = AddComment(bug1, rene, unix, "message8")
+	_, err = AddComment(bug1, rene, unix, "message8")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message9")
+	_, err = AddComment(bug1, rene, unix, "message9")
 	assert.Nil(t, err)
-	err = AddComment(bug1, rene, unix, "message10")
+	_, err = AddComment(bug1, rene, unix, "message10")
 	assert.Nil(t, err)
 	err = bug1.Commit(repoA)
 	assert.Nil(t, err)
@@ -317,29 +317,29 @@ func _RebaseConflict(t testing.TB) {
 	bug2, err := ReadLocalBug(repoB, bug1.Id())
 	assert.Nil(t, err)
 
-	err = AddComment(bug2, rene, unix, "message11")
+	_, err = AddComment(bug2, rene, unix, "message11")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message12")
+	_, err = AddComment(bug2, rene, unix, "message12")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message13")
+	_, err = AddComment(bug2, rene, unix, "message13")
 	assert.Nil(t, err)
 	err = bug2.Commit(repoB)
 	assert.Nil(t, err)
 
-	err = AddComment(bug2, rene, unix, "message14")
+	_, err = AddComment(bug2, rene, unix, "message14")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message15")
+	_, err = AddComment(bug2, rene, unix, "message15")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message16")
+	_, err = AddComment(bug2, rene, unix, "message16")
 	assert.Nil(t, err)
 	err = bug2.Commit(repoB)
 	assert.Nil(t, err)
 
-	err = AddComment(bug2, rene, unix, "message17")
+	_, err = AddComment(bug2, rene, unix, "message17")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message18")
+	_, err = AddComment(bug2, rene, unix, "message18")
 	assert.Nil(t, err)
-	err = AddComment(bug2, rene, unix, "message19")
+	_, err = AddComment(bug2, rene, unix, "message19")
 	assert.Nil(t, err)
 	err = bug2.Commit(repoB)
 	assert.Nil(t, err)
  
  
  
    
    @@ -83,15 +83,15 @@ type AddCommentTimelineItem struct {
 }
 
 // Convenience function to apply the operation
-func AddComment(b Interface, author Person, unixTime int64, message string) error {
+func AddComment(b Interface, author Person, unixTime int64, message string) (*AddCommentOperation, error) {
 	return AddCommentWithFiles(b, author, unixTime, message, nil)
 }
 
-func AddCommentWithFiles(b Interface, author Person, unixTime int64, message string, files []git.Hash) error {
+func AddCommentWithFiles(b Interface, author Person, unixTime int64, message string, files []git.Hash) (*AddCommentOperation, error) {
 	addCommentOp := NewAddCommentOp(author, unixTime, message, files)
 	if err := addCommentOp.Validate(); err != nil {
-		return err
+		return nil, err
 	}
 	b.Append(addCommentOp)
-	return nil
+	return addCommentOp, nil
 }
  
  
  
    
    @@ -96,19 +96,19 @@ type CreateTimelineItem struct {
 }
 
 // Convenience function to apply the operation
-func Create(author Person, unixTime int64, title, message string) (*Bug, error) {
+func Create(author Person, unixTime int64, title, message string) (*Bug, *CreateOperation, error) {
 	return CreateWithFiles(author, unixTime, title, message, nil)
 }
 
-func CreateWithFiles(author Person, unixTime int64, title, message string, files []git.Hash) (*Bug, error) {
+func CreateWithFiles(author Person, unixTime int64, title, message string, files []git.Hash) (*Bug, *CreateOperation, error) {
 	newBug := NewBug()
 	createOp := NewCreateOp(author, unixTime, title, message, files)
 
 	if err := createOp.Validate(); err != nil {
-		return nil, err
+		return nil, createOp, err
 	}
 
 	newBug.Append(createOp)
 
-	return newBug, nil
+	return newBug, createOp, nil
 }
  
  
  
    
    @@ -106,15 +106,15 @@ func NewEditCommentOp(author Person, unixTime int64, target git.Hash, message st
 }
 
 // Convenience function to apply the operation
-func EditComment(b Interface, author Person, unixTime int64, target git.Hash, message string) error {
+func EditComment(b Interface, author Person, unixTime int64, target git.Hash, message string) (*EditCommentOperation, error) {
 	return EditCommentWithFiles(b, author, unixTime, target, message, nil)
 }
 
-func EditCommentWithFiles(b Interface, author Person, unixTime int64, target git.Hash, message string, files []git.Hash) error {
+func EditCommentWithFiles(b Interface, author Person, unixTime int64, target git.Hash, message string, files []git.Hash) (*EditCommentOperation, error) {
 	editCommentOp := NewEditCommentOp(author, unixTime, target, message, files)
 	if err := editCommentOp.Validate(); err != nil {
-		return err
+		return nil, err
 	}
 	b.Append(editCommentOp)
-	return nil
+	return editCommentOp, nil
 }
  
  
  
    
    @@ -118,7 +118,7 @@ func (l LabelChangeTimelineItem) Hash() git.Hash {
 }
 
 // ChangeLabels is a convenience function to apply the operation
-func ChangeLabels(b Interface, author Person, unixTime int64, add, remove []string) ([]LabelChangeResult, error) {
+func ChangeLabels(b Interface, author Person, unixTime int64, add, remove []string) ([]LabelChangeResult, *LabelChangeOperation, error) {
 	var added, removed []Label
 	var results []LabelChangeResult
 
@@ -163,18 +163,18 @@ func ChangeLabels(b Interface, author Person, unixTime int64, add, remove []stri
 	}
 
 	if len(added) == 0 && len(removed) == 0 {
-		return results, fmt.Errorf("no label added or removed")
+		return results, nil, fmt.Errorf("no label added or removed")
 	}
 
 	labelOp := NewLabelChangeOperation(author, unixTime, added, removed)
 
 	if err := labelOp.Validate(); err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
 	b.Append(labelOp)
 
-	return results, nil
+	return results, labelOp, nil
 }
 
 func labelExist(labels []Label, label Label) bool {
  
  
  
    
    @@ -72,21 +72,21 @@ func (s SetStatusTimelineItem) Hash() git.Hash {
 }
 
 // Convenience function to apply the operation
-func Open(b Interface, author Person, unixTime int64) error {
+func Open(b Interface, author Person, unixTime int64) (*SetStatusOperation, error) {
 	op := NewSetStatusOp(author, unixTime, OpenStatus)
 	if err := op.Validate(); err != nil {
-		return err
+		return nil, err
 	}
 	b.Append(op)
-	return nil
+	return op, nil
 }
 
 // Convenience function to apply the operation
-func Close(b Interface, author Person, unixTime int64) error {
+func Close(b Interface, author Person, unixTime int64) (*SetStatusOperation, error) {
 	op := NewSetStatusOp(author, unixTime, ClosedStatus)
 	if err := op.Validate(); err != nil {
-		return err
+		return nil, err
 	}
 	b.Append(op)
-	return nil
+	return op, nil
 }
  
  
  
    
    @@ -95,7 +95,7 @@ func (s SetTitleTimelineItem) Hash() git.Hash {
 }
 
 // Convenience function to apply the operation
-func SetTitle(b Interface, author Person, unixTime int64, title string) error {
+func SetTitle(b Interface, author Person, unixTime int64, title string) (*SetTitleOperation, error) {
 	it := NewOperationIterator(b)
 
 	var lastTitleOp Operation
@@ -116,9 +116,9 @@ func SetTitle(b Interface, author Person, unixTime int64, title string) error {
 	setTitleOp := NewSetTitleOp(author, unixTime, title, was)
 
 	if err := setTitleOp.Validate(); err != nil {
-		return err
+		return nil, err
 	}
 
 	b.Append(setTitleOp)
-	return nil
+	return setTitleOp, nil
 }
  
  
  
    
    @@ -49,11 +49,15 @@ func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error {
 }
 
 func (c *BugCache) AddCommentRaw(author bug.Person, unixTime int64, message string, files []git.Hash, metadata map[string]string) error {
-	err := bug.AddCommentWithFiles(c.bug, author, unixTime, message, files)
+	op, err := bug.AddCommentWithFiles(c.bug, author, unixTime, message, files)
 	if err != nil {
 		return err
 	}
 
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
+
 	return c.notifyUpdated()
 }
 
@@ -63,15 +67,19 @@ func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelCh
 		return nil, err
 	}
 
-	return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed)
+	return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil)
 }
 
-func (c *BugCache) ChangeLabelsRaw(author bug.Person, unixTime int64, added []string, removed []string) ([]bug.LabelChangeResult, error) {
-	changes, err := bug.ChangeLabels(c.bug, author, unixTime, added, removed)
+func (c *BugCache) ChangeLabelsRaw(author bug.Person, 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
 	}
 
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
+
 	err = c.notifyUpdated()
 	if err != nil {
 		return nil, err
@@ -86,15 +94,19 @@ func (c *BugCache) Open() error {
 		return err
 	}
 
-	return c.OpenRaw(author, time.Now().Unix())
+	return c.OpenRaw(author, time.Now().Unix(), nil)
 }
 
-func (c *BugCache) OpenRaw(author bug.Person, unixTime int64) error {
-	err := bug.Open(c.bug, author, unixTime)
+func (c *BugCache) OpenRaw(author bug.Person, unixTime int64, metadata map[string]string) error {
+	op, err := bug.Open(c.bug, author, unixTime)
 	if err != nil {
 		return err
 	}
 
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
+
 	return c.notifyUpdated()
 }
 
@@ -104,15 +116,19 @@ func (c *BugCache) Close() error {
 		return err
 	}
 
-	return c.CloseRaw(author, time.Now().Unix())
+	return c.CloseRaw(author, time.Now().Unix(), nil)
 }
 
-func (c *BugCache) CloseRaw(author bug.Person, unixTime int64) error {
-	err := bug.Close(c.bug, author, unixTime)
+func (c *BugCache) CloseRaw(author bug.Person, unixTime int64, metadata map[string]string) error {
+	op, err := bug.Close(c.bug, author, unixTime)
 	if err != nil {
 		return err
 	}
 
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
+
 	return c.notifyUpdated()
 }
 
@@ -122,15 +138,41 @@ func (c *BugCache) SetTitle(title string) error {
 		return err
 	}
 
-	return c.SetTitleRaw(author, time.Now().Unix(), title)
+	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 {
+	op, err := bug.SetTitle(c.bug, author, unixTime, title)
+	if err != nil {
+		return err
+	}
+
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
+
+	return c.notifyUpdated()
+}
+
+func (c *BugCache) EditComment(target git.Hash, message string) error {
+	author, err := bug.GetUser(c.repoCache.repo)
+	if err != nil {
+		return err
+	}
+
+	return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil)
 }
 
-func (c *BugCache) SetTitleRaw(author bug.Person, unixTime int64, title string) error {
-	err := bug.SetTitle(c.bug, author, unixTime, title)
+func (c *BugCache) EditCommentRaw(author bug.Person, 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
 	}
 
+	for key, value := range metadata {
+		op.SetMetadata(key, value)
+	}
+
 	return c.notifyUpdated()
 }
 
  
  
  
    
    @@ -364,13 +364,13 @@ func (c *RepoCache) NewBugWithFiles(title string, message string, files []git.Ha
 // 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) {
-	b, err := bug.CreateWithFiles(author, unixTime, title, message, files)
+	b, op, err := bug.CreateWithFiles(author, unixTime, title, message, files)
 	if err != nil {
 		return nil, err
 	}
 
 	for key, value := range metadata {
-		b.FirstOp().SetMetadata(key, value)
+		op.SetMetadata(key, value)
 	}
 
 	err = b.Commit(c.repo)
  
  
  
    
    @@ -65,7 +65,7 @@ func GenerateRandomBugsWithSeed(opts Options, seed int64) []*bug.Bug {
 	for i := 0; i < opts.BugNumber; i++ {
 		addedLabels = []string{}
 
-		b, err := bug.Create(
+		b, _, err := bug.Create(
 			randomPerson(opts.PersonNumber),
 			time.Now().Unix(),
 			fake.Sentence(),
@@ -163,19 +163,19 @@ func paragraphs() string {
 }
 
 func comment(b bug.Interface, p bug.Person) {
-	_ = bug.AddComment(b, p, time.Now().Unix(), paragraphs())
+	_, _ = bug.AddComment(b, p, time.Now().Unix(), paragraphs())
 }
 
 func title(b bug.Interface, p bug.Person) {
-	_ = bug.SetTitle(b, p, time.Now().Unix(), fake.Sentence())
+	_, _ = bug.SetTitle(b, p, time.Now().Unix(), fake.Sentence())
 }
 
 func open(b bug.Interface, p bug.Person) {
-	_ = bug.Open(b, p, time.Now().Unix())
+	_, _ = bug.Open(b, p, time.Now().Unix())
 }
 
 func close(b bug.Interface, p bug.Person) {
-	_ = bug.Close(b, p, time.Now().Unix())
+	_, _ = bug.Close(b, p, time.Now().Unix())
 }
 
 var addedLabels []string
@@ -202,5 +202,5 @@ func labels(b bug.Interface, p bug.Person) {
 	// ignore error
 	// if the randomisation produce no changes, no op
 	// is added to the bug
-	_, _ = bug.ChangeLabels(b, p, time.Now().Unix(), added, removed)
+	_, _, _ = bug.ChangeLabels(b, p, time.Now().Unix(), added, removed)
 }