Detailed changes
  
  
    
    @@ -1,8 +1,6 @@
 package cache
 
 import (
-	"io"
-
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/operations"
 	"github.com/MichaelMure/git-bug/util/git"
@@ -51,18 +49,23 @@ func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error {
 	return c.notifyUpdated()
 }
 
-func (c *BugCache) ChangeLabels(out io.Writer, added []string, removed []string) error {
+func (c *BugCache) ChangeLabels(added []string, removed []string) ([]operations.LabelChangeResult, error) {
 	author, err := bug.GetUser(c.repoCache.repo)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
-	err = operations.ChangeLabels(out, c.bug, author, added, removed)
+	changes, err := operations.ChangeLabels(c.bug, author, added, removed)
 	if err != nil {
-		return err
+		return changes, err
 	}
 
-	return c.notifyUpdated()
+	err = c.notifyUpdated()
+	if err != nil {
+		return nil, err
+	}
+
+	return changes, nil
 }
 
 func (c *BugCache) Open() error {
  
  
  
    
    @@ -2,9 +2,10 @@ package commands
 
 import (
 	"errors"
-	"os"
+	"fmt"
 
 	"github.com/MichaelMure/git-bug/cache"
+	"github.com/MichaelMure/git-bug/operations"
 	"github.com/spf13/cobra"
 )
 
@@ -40,7 +41,25 @@ func runLabel(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	err = b.ChangeLabels(os.Stdout, add, remove)
+	changes, err := b.ChangeLabels(add, remove)
+
+	for _, change := range changes {
+		switch change.Status {
+		case operations.LabelChangeAdded:
+			fmt.Printf("label %s added\n", change.Label)
+		case operations.LabelChangeRemoved:
+			fmt.Printf("label %s removed\n", change.Label)
+		case operations.LabelChangeDuplicateInOp:
+			fmt.Printf("label %s is a duplicate\n", change.Label)
+		case operations.LabelChangeAlreadySet:
+			fmt.Printf("label %s was already set\n", change.Label)
+		case operations.LabelChangeDoesntExist:
+			fmt.Printf("label %s doesn't exist on this bug\n", change.Label)
+		default:
+			panic(fmt.Sprintf("unknown label change status %v", change.Status))
+		}
+	}
+
 	if err != nil {
 		return err
 	}
  
  
  
    
    @@ -89,7 +89,7 @@ func (r mutationResolver) ChangeLabels(ctx context.Context, repoRef *string, pre
 		return bug.Snapshot{}, err
 	}
 
-	err = b.ChangeLabels(nil, added, removed)
+	_, err = b.ChangeLabels(added, removed)
 	if err != nil {
 		return bug.Snapshot{}, err
 	}
  
  
  
    
    @@ -165,6 +165,7 @@ func labels(b bug.Interface, p bug.Person) {
 		removed = append(removed, addedLabels[index])
 		addedLabels[index] = addedLabels[len(addedLabels)-1]
 		addedLabels = addedLabels[:len(addedLabels)-1]
+		nbRemoved--
 	}
 
 	var added []string
@@ -175,5 +176,8 @@ func labels(b bug.Interface, p bug.Person) {
 		addedLabels = append(addedLabels, label)
 	}
 
-	operations.ChangeLabels(nil, b, p, added, removed)
+	// ignore error
+	// if the randomisation produce no changes, no op
+	// is added to the bug
+	operations.ChangeLabels(b, p, added, removed)
 }
  
  
  
    
    @@ -2,8 +2,6 @@ package operations
 
 import (
 	"fmt"
-	"io"
-	"io/ioutil"
 	"sort"
 
 	"github.com/MichaelMure/git-bug/bug"
@@ -60,13 +58,9 @@ func NewLabelChangeOperation(author bug.Person, added, removed []bug.Label) Labe
 }
 
 // ChangeLabels is a convenience function to apply the operation
-func ChangeLabels(out io.Writer, b bug.Interface, author bug.Person, add, remove []string) error {
-	// TODO: return a channel of result (like MergeAll) instead of formatting the result for the upper layers
+func ChangeLabels(b bug.Interface, author bug.Person, add, remove []string) ([]LabelChangeResult, error) {
 	var added, removed []bug.Label
-
-	if out == nil {
-		out = ioutil.Discard
-	}
+	var results []LabelChangeResult
 
 	snap := b.Compile()
 
@@ -75,17 +69,18 @@ func ChangeLabels(out io.Writer, b bug.Interface, author bug.Person, add, remove
 
 		// check for duplicate
 		if labelExist(added, label) {
-			fmt.Fprintf(out, "label \"%s\" is a duplicate\n", str)
+			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDuplicateInOp})
 			continue
 		}
 
 		// check that the label doesn't already exist
 		if labelExist(snap.Labels, label) {
-			fmt.Fprintf(out, "label \"%s\" is already set on this bug\n", str)
+			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeAlreadySet})
 			continue
 		}
 
 		added = append(added, label)
+		results = append(results, LabelChangeResult{Label: label, Status: LabelChangeAdded})
 	}
 
 	for _, str := range remove {
@@ -93,28 +88,29 @@ func ChangeLabels(out io.Writer, b bug.Interface, author bug.Person, add, remove
 
 		// check for duplicate
 		if labelExist(removed, label) {
-			fmt.Fprintf(out, "label \"%s\" is a duplicate\n", str)
+			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDuplicateInOp})
 			continue
 		}
 
 		// check that the label actually exist
 		if !labelExist(snap.Labels, label) {
-			fmt.Fprintf(out, "label \"%s\" doesn't exist on this bug\n", str)
+			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDoesntExist})
 			continue
 		}
 
 		removed = append(removed, label)
+		results = append(results, LabelChangeResult{Label: label, Status: LabelChangeRemoved})
 	}
 
 	if len(added) == 0 && len(removed) == 0 {
-		return fmt.Errorf("no label added or removed")
+		return results, fmt.Errorf("no label added or removed")
 	}
 
 	labelOp := NewLabelChangeOperation(author, added, removed)
 
 	b.Append(labelOp)
 
-	return nil
+	return results, nil
 }
 
 func labelExist(labels []bug.Label, label bug.Label) bool {
@@ -126,3 +122,19 @@ func labelExist(labels []bug.Label, label bug.Label) bool {
 
 	return false
 }
+
+type LabelChangeStatus int
+
+const (
+	_ LabelChangeStatus = iota
+	LabelChangeAdded
+	LabelChangeRemoved
+	LabelChangeDuplicateInOp
+	LabelChangeAlreadySet
+	LabelChangeDoesntExist
+)
+
+type LabelChangeResult struct {
+	Label  bug.Label
+	Status LabelChangeStatus
+}
  
  
  
    
    @@ -600,7 +600,7 @@ func (sb *showBug) addLabel(g *gocui.Gui, v *gocui.View) error {
 			return r == ' ' || r == ','
 		})
 
-		err := sb.bug.ChangeLabels(nil, trimLabels(labels), nil)
+		_, err := sb.bug.ChangeLabels(trimLabels(labels), nil)
 		if err != nil {
 			ui.msgPopup.Activate(msgPopupErrorTitle, err.Error())
 		}
@@ -623,7 +623,7 @@ func (sb *showBug) removeLabel(g *gocui.Gui, v *gocui.View) error {
 			return r == ' ' || r == ','
 		})
 
-		err := sb.bug.ChangeLabels(nil, nil, trimLabels(labels))
+		_, err := sb.bug.ChangeLabels(nil, trimLabels(labels))
 		if err != nil {
 			ui.msgPopup.Activate(msgPopupErrorTitle, err.Error())
 		}