Detailed changes
  
  
    
    @@ -1,6 +1,7 @@
 package bug
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
 	"strings"
@@ -193,20 +194,21 @@ func readBug(repo repository.Repo, ref string) (*Bug, error) {
 			return nil, err
 		}
 
-		op, err := ParseOperationPack(data)
+		opp := &OperationPack{}
+		err = json.Unmarshal(data, &opp)
 
 		if err != nil {
 			return nil, err
 		}
 
 		// tag the pack with the commit hash
-		op.commitHash = hash
+		opp.commitHash = hash
 
 		if err != nil {
 			return nil, err
 		}
 
-		bug.packs = append(bug.packs, *op)
+		bug.packs = append(bug.packs, *opp)
 	}
 
 	return &bug, nil
@@ -450,7 +452,7 @@ func makeMediaTree(pack OperationPack) []repository.TreeEntry {
 	added := make(map[git.Hash]interface{})
 
 	for _, ops := range pack.Operations {
-		for _, file := range ops.Files() {
+		for _, file := range ops.GetFiles() {
 			if _, has := added[file]; !has {
 				tree = append(tree, repository.TreeEntry{
 					ObjectType: repository.Blob,
  
  
  
    
    @@ -27,8 +27,8 @@ type Operation interface {
 	GetUnixTime() int64
 	// Apply the operation to a Snapshot to create the final state
 	Apply(snapshot Snapshot) Snapshot
-	// Files return the files needed by this operation
-	Files() []git.Hash
+	// GetFiles return the files needed by this operation
+	GetFiles() []git.Hash
 
 	// TODO: data validation (ex: a title is a single line)
 	// Validate() bool
@@ -36,9 +36,9 @@ type Operation interface {
 
 // OpBase implement the common code for all operations
 type OpBase struct {
-	OperationType OperationType
-	Author        Person
-	UnixTime      int64
+	OperationType OperationType `json:"type"`
+	Author        Person        `json:"author"`
+	UnixTime      int64         `json:"timestamp"`
 }
 
 // NewOpBase is the constructor for an OpBase
@@ -65,7 +65,7 @@ func (op OpBase) GetUnixTime() int64 {
 	return op.UnixTime
 }
 
-// Files return the files needed by this operation
-func (op OpBase) Files() []git.Hash {
+// GetFiles return the files needed by this operation
+func (op OpBase) GetFiles() []git.Hash {
 	return nil
 }
  
  
  
    
    @@ -1,13 +1,16 @@
 package bug
 
 import (
-	"bytes"
-	"encoding/gob"
+	"encoding/json"
+	"fmt"
+	"reflect"
 
 	"github.com/MichaelMure/git-bug/repository"
 	"github.com/MichaelMure/git-bug/util/git"
 )
 
+const formatVersion = 1
+
 // OperationPack represent an ordered set of operation to apply
 // to a Bug. These operations are stored in a single Git commit.
 //
@@ -21,34 +24,65 @@ type OperationPack struct {
 	commitHash git.Hash
 }
 
-// ParseOperationPack will deserialize an OperationPack from raw bytes
-func ParseOperationPack(data []byte) (*OperationPack, error) {
-	reader := bytes.NewReader(data)
-	decoder := gob.NewDecoder(reader)
+var operations map[OperationType]reflect.Type
 
-	var opp OperationPack
+func Register(t OperationType, op interface{}) {
+	if operations == nil {
+		operations = make(map[OperationType]reflect.Type)
+	}
+	operations[t] = reflect.TypeOf(op)
+}
 
-	err := decoder.Decode(&opp)
+func (opp *OperationPack) MarshalJSON() ([]byte, error) {
+	return json.Marshal(struct {
+		Version    uint        `json:"version"`
+		Operations []Operation `json:"ops"`
+	}{
+		Version:    formatVersion,
+		Operations: opp.Operations,
+	})
+}
 
-	if err != nil {
-		return nil, err
+func (opp *OperationPack) UnmarshalJSON(data []byte) error {
+	aux := struct {
+		Version    uint              `json:"version"`
+		Operations []json.RawMessage `json:"ops"`
+	}{}
+
+	if err := json.Unmarshal(data, &aux); err != nil {
+		return err
 	}
 
-	return &opp, nil
-}
+	if aux.Version != formatVersion {
+		return fmt.Errorf("unknown format version %v", aux.Version)
+	}
 
-// Serialize will serialise an OperationPack into raw bytes
-func (opp *OperationPack) Serialize() ([]byte, error) {
-	var data bytes.Buffer
+	for _, raw := range aux.Operations {
+		var t struct {
+			OperationType OperationType `json:"type"`
+		}
 
-	encoder := gob.NewEncoder(&data)
-	err := encoder.Encode(*opp)
+		if err := json.Unmarshal(raw, &t); err != nil {
+			return err
+		}
 
-	if err != nil {
-		return nil, err
+		opType, ok := operations[t.OperationType]
+		if !ok {
+			return fmt.Errorf("unknown operation type %v", t.OperationType)
+		}
+
+		op := reflect.New(opType).Interface()
+
+		if err := json.Unmarshal(raw, op); err != nil {
+			return err
+		}
+
+		deref := reflect.ValueOf(op).Elem().Interface()
+
+		opp.Operations = append(opp.Operations, deref.(Operation))
 	}
 
-	return data.Bytes(), nil
+	return nil
 }
 
 // Append a new operation to the pack
@@ -69,7 +103,7 @@ func (opp *OperationPack) IsValid() bool {
 // Write will serialize and store the OperationPack as a git blob and return
 // its hash
 func (opp *OperationPack) Write(repo repository.Repo) (git.Hash, error) {
-	data, err := opp.Serialize()
+	data, err := json.Marshal(opp)
 
 	if err != nil {
 		return "", err
  
  
  
    
    @@ -8,8 +8,8 @@ import (
 )
 
 type Person struct {
-	Name  string
-	Email string
+	Name  string `json:"name"`
+	Email string `json:"email"`
 }
 
 // GetUser will query the repository for user detail and build the corresponding Person
  
  
  
    
    @@ -340,7 +340,7 @@ func (ec *executionContext) _AddCommentOperation_files(ctx context.Context, fiel
 	rctx.Field = field
 	rctx.PushField(field.Alias)
 	defer rctx.Pop()
-	res := obj.Files()
+	res := obj.Files
 	arr1 := graphql.Array{}
 	for idx1 := range res {
 		arr1 = append(arr1, func() graphql.Marshaler {
@@ -1157,7 +1157,7 @@ func (ec *executionContext) _CreateOperation_files(ctx context.Context, field gr
 	rctx.Field = field
 	rctx.PushField(field.Alias)
 	defer rctx.Pop()
-	res := obj.Files()
+	res := obj.Files
 	arr1 := graphql.Array{}
 	for idx1 := range res {
 		arr1 = append(arr1, func() graphql.Marshaler {
  
  
  
    
    @@ -11,16 +11,16 @@ var _ bug.Operation = AddCommentOperation{}
 
 type AddCommentOperation struct {
 	bug.OpBase
-	Message string
+	Message string `json:"message"`
 	// TODO: change for a map[string]util.hash to store the filename ?
-	files []git.Hash
+	Files []git.Hash `json:"files"`
 }
 
 func (op AddCommentOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
 	comment := bug.Comment{
 		Message:  op.Message,
 		Author:   op.Author,
-		Files:    op.files,
+		Files:    op.Files,
 		UnixTime: op.UnixTime,
 	}
 
@@ -29,15 +29,15 @@ func (op AddCommentOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
 	return snapshot
 }
 
-func (op AddCommentOperation) Files() []git.Hash {
-	return op.files
+func (op AddCommentOperation) GetFiles() []git.Hash {
+	return op.Files
 }
 
 func NewAddCommentOp(author bug.Person, message string, files []git.Hash) AddCommentOperation {
 	return AddCommentOperation{
 		OpBase:  bug.NewOpBase(bug.AddCommentOp, author),
 		Message: message,
-		files:   files,
+		Files:   files,
 	}
 }
 
  
  
  
    
    @@ -11,9 +11,9 @@ var _ bug.Operation = CreateOperation{}
 
 type CreateOperation struct {
 	bug.OpBase
-	Title   string
-	Message string
-	files   []git.Hash
+	Title   string     `json:"title"`
+	Message string     `json:"message"`
+	Files   []git.Hash `json:"files"`
 }
 
 func (op CreateOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
@@ -30,8 +30,8 @@ func (op CreateOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
 	return snapshot
 }
 
-func (op CreateOperation) Files() []git.Hash {
-	return op.files
+func (op CreateOperation) GetFiles() []git.Hash {
+	return op.Files
 }
 
 func NewCreateOp(author bug.Person, title, message string, files []git.Hash) CreateOperation {
@@ -39,7 +39,7 @@ func NewCreateOp(author bug.Person, title, message string, files []git.Hash) Cre
 		OpBase:  bug.NewOpBase(bug.CreateOp, author),
 		Title:   title,
 		Message: message,
-		files:   files,
+		Files:   files,
 	}
 }
 
  
  
  
    
    @@ -14,8 +14,8 @@ var _ bug.Operation = LabelChangeOperation{}
 // LabelChangeOperation define a Bug operation to add or remove labels
 type LabelChangeOperation struct {
 	bug.OpBase
-	Added   []bug.Label
-	Removed []bug.Label
+	Added   []bug.Label `json:"added"`
+	Removed []bug.Label `json:"removed"`
 }
 
 // Apply apply the operation
  
  
  
    
    @@ -1,12 +1,14 @@
 package operations
 
-import "encoding/gob"
+import (
+	"github.com/MichaelMure/git-bug/bug"
+)
 
 // Package initialisation used to register operation's type for (de)serialization
 func init() {
-	gob.Register(AddCommentOperation{})
-	gob.Register(CreateOperation{})
-	gob.Register(SetTitleOperation{})
-	gob.Register(SetStatusOperation{})
-	gob.Register(LabelChangeOperation{})
+	bug.Register(bug.CreateOp, CreateOperation{})
+	bug.Register(bug.SetTitleOp, SetTitleOperation{})
+	bug.Register(bug.AddCommentOp, AddCommentOperation{})
+	bug.Register(bug.SetStatusOp, SetStatusOperation{})
+	bug.Register(bug.LabelChangeOp, LabelChangeOperation{})
 }
  
  
  
    
    @@ -10,7 +10,7 @@ var _ bug.Operation = SetStatusOperation{}
 
 type SetStatusOperation struct {
 	bug.OpBase
-	Status bug.Status
+	Status bug.Status `json:"status"`
 }
 
 func (op SetStatusOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
  
  
  
    
    @@ -10,8 +10,8 @@ var _ bug.Operation = SetTitleOperation{}
 
 type SetTitleOperation struct {
 	bug.OpBase
-	Title string
-	Was   string
+	Title string `json:"title"`
+	Was   string `json:"was"`
 }
 
 func (op SetTitleOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
  
  
  
    
    @@ -1,30 +1,34 @@
 package tests
 
 import (
-	"github.com/MichaelMure/git-bug/bug"
+	"encoding/json"
+	"reflect"
 	"testing"
+
+	"github.com/MichaelMure/git-bug/bug"
 )
 
 func TestOperationPackSerialize(t *testing.T) {
-	opp := bug.OperationPack{}
+	opp := &bug.OperationPack{}
 
 	opp.Append(createOp)
 	opp.Append(setTitleOp)
 	opp.Append(addCommentOp)
+	opp.Append(setStatusOp)
+	opp.Append(labelChangeOp)
 
-	data, err := opp.Serialize()
-
+	data, err := json.Marshal(opp)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	if len(data) == 0 {
-		t.Fatal("empty serialized data")
-	}
-
-	_, err = bug.ParseOperationPack(data)
-
+	var opp2 *bug.OperationPack
+	err = json.Unmarshal(data, &opp2)
 	if err != nil {
 		t.Fatal(err)
 	}
+
+	if !reflect.DeepEqual(opp, opp2) {
+		t.Fatalf("%v and %v are different", opp, opp2)
+	}
 }