aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bug/op_add_comment.go2
-rw-r--r--bug/op_create.go2
-rw-r--r--bug/op_edit_comment.go2
-rw-r--r--bug/operation.go17
-rw-r--r--entity/dag/common_test.go15
-rw-r--r--entity/dag/operation.go9
-rw-r--r--entity/dag/operation_pack.go45
-rw-r--r--entity/dag/operation_pack_test.go20
8 files changed, 91 insertions, 21 deletions
diff --git a/bug/op_add_comment.go b/bug/op_add_comment.go
index 4cba200f..f835866b 100644
--- a/bug/op_add_comment.go
+++ b/bug/op_add_comment.go
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/entity/dag"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/text"
@@ -12,6 +13,7 @@ import (
)
var _ Operation = &AddCommentOperation{}
+var _ dag.OperationWithFiles = &AddCommentOperation{}
// AddCommentOperation will add a new comment in the bug
type AddCommentOperation struct {
diff --git a/bug/op_create.go b/bug/op_create.go
index 37e1ddc5..75b60bd8 100644
--- a/bug/op_create.go
+++ b/bug/op_create.go
@@ -6,6 +6,7 @@ import (
"strings"
"github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/entity/dag"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/text"
@@ -13,6 +14,7 @@ import (
)
var _ Operation = &CreateOperation{}
+var _ dag.OperationWithFiles = &CreateOperation{}
// CreateOperation define the initial creation of a bug
type CreateOperation struct {
diff --git a/bug/op_edit_comment.go b/bug/op_edit_comment.go
index 653ab71e..3e6634e4 100644
--- a/bug/op_edit_comment.go
+++ b/bug/op_edit_comment.go
@@ -7,6 +7,7 @@ import (
"github.com/pkg/errors"
"github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/entity/dag"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/timestamp"
@@ -15,6 +16,7 @@ import (
)
var _ Operation = &EditCommentOperation{}
+var _ dag.OperationWithFiles = &EditCommentOperation{}
// EditCommentOperation will change a comment in the bug
type EditCommentOperation struct {
diff --git a/bug/operation.go b/bug/operation.go
index d01f1cc9..8daa2cde 100644
--- a/bug/operation.go
+++ b/bug/operation.go
@@ -11,7 +11,6 @@ import (
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/entity/dag"
"github.com/MichaelMure/git-bug/identity"
- "github.com/MichaelMure/git-bug/repository"
)
// OperationType is an operation type identifier
@@ -38,10 +37,9 @@ type Operation interface {
// Time return the time when the operation was added
Time() time.Time
- // GetFiles return the files needed by this operation
- GetFiles() []repository.Hash
// Apply the operation to a Snapshot to create the final state
Apply(snapshot *Snapshot)
+
// SetMetadata store arbitrary metadata about the operation
SetMetadata(key string, value string)
// GetMetadata retrieve arbitrary metadata about the operation
@@ -212,11 +210,6 @@ func (base *OpBase) Time() time.Time {
return time.Unix(base.UnixTime, 0)
}
-// GetFiles return the files needed by this operation
-func (base *OpBase) GetFiles() []repository.Hash {
- return nil
-}
-
// Validate check the OpBase for errors
func (base *OpBase) Validate(op Operation, opType OperationType) error {
if base.OperationType != opType {
@@ -235,9 +228,11 @@ func (base *OpBase) Validate(op Operation, opType OperationType) error {
return errors.Wrap(err, "author")
}
- for _, hash := range op.GetFiles() {
- if !hash.IsValid() {
- return fmt.Errorf("file with invalid hash %v", hash)
+ if op, ok := op.(dag.OperationWithFiles); ok {
+ for _, hash := range op.GetFiles() {
+ if !hash.IsValid() {
+ return fmt.Errorf("file with invalid hash %v", hash)
+ }
}
}
diff --git a/entity/dag/common_test.go b/entity/dag/common_test.go
index fa15cd1f..1898451d 100644
--- a/entity/dag/common_test.go
+++ b/entity/dag/common_test.go
@@ -23,10 +23,11 @@ type op1 struct {
OperationType int `json:"type"`
Field1 string `json:"field_1"`
+ Files []repository.Hash
}
-func newOp1(author identity.Interface, field1 string) *op1 {
- return &op1{author: author, OperationType: 1, Field1: field1}
+func newOp1(author identity.Interface, field1 string, files ...repository.Hash) *op1 {
+ return &op1{author: author, OperationType: 1, Field1: field1, Files: files}
}
func (o *op1) Id() entity.Id {
@@ -34,11 +35,15 @@ func (o *op1) Id() entity.Id {
return entity.DeriveId(data)
}
+func (o *op1) Validate() error { return nil }
+
func (o *op1) Author() identity.Interface {
return o.author
}
-func (o *op1) Validate() error { return nil }
+func (o *op1) GetFiles() []repository.Hash {
+ return o.Files
+}
type op2 struct {
author identity.Interface
@@ -56,12 +61,12 @@ func (o *op2) Id() entity.Id {
return entity.DeriveId(data)
}
+func (o *op2) Validate() error { return nil }
+
func (o *op2) Author() identity.Interface {
return o.author
}
-func (o *op2) Validate() error { return nil }
-
func unmarshaler(author identity.Interface, raw json.RawMessage) (Operation, error) {
var t struct {
OperationType int `json:"type"`
diff --git a/entity/dag/operation.go b/entity/dag/operation.go
index 94974a82..1bfb3d3d 100644
--- a/entity/dag/operation.go
+++ b/entity/dag/operation.go
@@ -3,6 +3,7 @@ package dag
import (
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
+ "github.com/MichaelMure/git-bug/repository"
)
// Operation is a piece of data defining a change to reflect on the state of an Entity.
@@ -33,3 +34,11 @@ type Operation interface {
// Author returns the author of this operation
Author() identity.Interface
}
+
+// OperationWithFiles is an extended Operation that has files dependency, stored in git.
+type OperationWithFiles interface {
+ Operation
+
+ // GetFiles return the files needed by this operation
+ GetFiles() []repository.Hash
+}
diff --git a/entity/dag/operation_pack.go b/entity/dag/operation_pack.go
index a436fd33..72063c60 100644
--- a/entity/dag/operation_pack.go
+++ b/entity/dag/operation_pack.go
@@ -15,10 +15,8 @@ import (
"github.com/MichaelMure/git-bug/util/lamport"
)
-// TODO: extra data tree
-const extraEntryName = "extra"
-
const opsEntryName = "ops"
+const extraEntryName = "extra"
const versionEntryPrefix = "version-"
const createClockEntryPrefix = "create-clock-"
const editClockEntryPrefix = "edit-clock-"
@@ -118,6 +116,7 @@ func (opp *operationPack) Write(def Definition, repo repository.Repo, parentComm
// Make a Git tree referencing this blob and encoding the other values:
// - format version
// - clocks
+ // - extra data
tree := []repository.TreeEntry{
{ObjectType: repository.Blob, Hash: emptyBlobHash,
Name: fmt.Sprintf(versionEntryPrefix+"%d", def.FormatVersion)},
@@ -133,6 +132,17 @@ func (opp *operationPack) Write(def Definition, repo repository.Repo, parentComm
Name: fmt.Sprintf(createClockEntryPrefix+"%d", opp.CreateTime),
})
}
+ if extraTree := opp.makeExtraTree(); len(extraTree) > 0 {
+ extraTreeHash, err := repo.StoreTree(extraTree)
+ if err != nil {
+ return "", err
+ }
+ tree = append(tree, repository.TreeEntry{
+ ObjectType: repository.Tree,
+ Hash: extraTreeHash,
+ Name: extraEntryName,
+ })
+ }
// Store the tree
treeHash, err := repo.StoreTree(tree)
@@ -163,6 +173,35 @@ func (opp *operationPack) Write(def Definition, repo repository.Repo, parentComm
return commitHash, nil
}
+func (opp *operationPack) makeExtraTree() []repository.TreeEntry {
+ var tree []repository.TreeEntry
+ counter := 0
+ added := make(map[repository.Hash]interface{})
+
+ for _, ops := range opp.Operations {
+ ops, ok := ops.(OperationWithFiles)
+ if !ok {
+ continue
+ }
+
+ for _, file := range ops.GetFiles() {
+ if _, has := added[file]; !has {
+ tree = append(tree, repository.TreeEntry{
+ ObjectType: repository.Blob,
+ Hash: file,
+ // The name is not important here, we only need to
+ // reference the blob.
+ Name: fmt.Sprintf("file%d", counter),
+ })
+ counter++
+ added[file] = struct{}{}
+ }
+ }
+ }
+
+ return tree
+}
+
// readOperationPack read the operationPack encoded in git at the given Tree hash.
//
// Validity of the Lamport clocks is left for the caller to decide.
diff --git a/entity/dag/operation_pack_test.go b/entity/dag/operation_pack_test.go
index a12382af..0fe98dc7 100644
--- a/entity/dag/operation_pack_test.go
+++ b/entity/dag/operation_pack_test.go
@@ -1,6 +1,7 @@
package dag
import (
+ "math/rand"
"testing"
"github.com/stretchr/testify/require"
@@ -11,10 +12,16 @@ import (
func TestOperationPackReadWrite(t *testing.T) {
repo, id1, _, resolver, def := makeTestContext()
+ blobHash1, err := repo.StoreData(randomData())
+ require.NoError(t, err)
+
+ blobHash2, err := repo.StoreData(randomData())
+ require.NoError(t, err)
+
opp := &operationPack{
Author: id1,
Operations: []Operation{
- newOp1(id1, "foo"),
+ newOp1(id1, "foo", blobHash1, blobHash2),
newOp2(id1, "bar"),
},
CreateTime: 123,
@@ -36,7 +43,7 @@ func TestOperationPackReadWrite(t *testing.T) {
opp3 := &operationPack{
Author: id1,
Operations: []Operation{
- newOp1(id1, "foo"),
+ newOp1(id1, "foo", blobHash1, blobHash2),
newOp2(id1, "bar"),
},
CreateTime: 123,
@@ -86,3 +93,12 @@ func TestOperationPackSignedReadWrite(t *testing.T) {
}
require.Equal(t, opp.Id(), opp3.Id())
}
+
+func randomData() []byte {
+ var letterRunes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ b := make([]byte, 32)
+ for i := range b {
+ b[i] = letterRunes[rand.Intn(len(letterRunes))]
+ }
+ return b
+}