aboutsummaryrefslogtreecommitdiffstats
path: root/bug
diff options
context:
space:
mode:
Diffstat (limited to 'bug')
-rw-r--r--bug/bug.go20
-rw-r--r--bug/interface.go52
-rw-r--r--bug/operation_iterator.go4
-rw-r--r--bug/operations/add_comment.go4
-rw-r--r--bug/operations/label_change.go2
-rw-r--r--bug/operations/set_status.go4
-rw-r--r--bug/operations/set_title.go2
-rw-r--r--bug/with_snapshot.go58
8 files changed, 130 insertions, 16 deletions
diff --git a/bug/bug.go b/bug/bug.go
index 6ef58786..1137ecfa 100644
--- a/bug/bug.go
+++ b/bug/bug.go
@@ -24,6 +24,8 @@ const editClockEntryPattern = "edit-clock-%d"
const idLength = 40
const humanIdLength = 7
+var _ Interface = &Bug{}
+
// Bug hold the data of a bug thread, organized in a way close to
// how it will be persisted inside Git. This is the data structure
// used to merge two different version of the same Bug.
@@ -468,25 +470,27 @@ func makeMediaTree(pack OperationPack) []repository.TreeEntry {
// Merge a different version of the same bug by rebasing operations of this bug
// that are not present in the other on top of the chain of operations of the
// other version.
-func (bug *Bug) Merge(repo repository.Repo, other *Bug) (bool, error) {
+func (bug *Bug) Merge(repo repository.Repo, other Interface) (bool, error) {
+ var otherBug = bugFromInterface(other)
+
// Note: a faster merge should be possible without actually reading and parsing
// all operations pack of our side.
// Reading the other side is still necessary to validate remote data, at least
// for new operations
- if bug.id != other.id {
+ if bug.id != otherBug.id {
return false, errors.New("merging unrelated bugs is not supported")
}
- if len(other.staging.Operations) > 0 {
+ if len(otherBug.staging.Operations) > 0 {
return false, errors.New("merging a bug with a non-empty staging is not supported")
}
- if bug.lastCommit == "" || other.lastCommit == "" {
+ if bug.lastCommit == "" || otherBug.lastCommit == "" {
return false, errors.New("can't merge a bug that has never been stored")
}
- ancestor, err := repo.FindCommonAncestor(bug.lastCommit, other.lastCommit)
+ ancestor, err := repo.FindCommonAncestor(bug.lastCommit, otherBug.lastCommit)
if err != nil {
return false, err
@@ -505,15 +509,15 @@ func (bug *Bug) Merge(repo repository.Repo, other *Bug) (bool, error) {
}
}
- if len(other.packs) == ancestorIndex+1 {
+ if len(otherBug.packs) == ancestorIndex+1 {
// Nothing to rebase, return early
return false, nil
}
// get other bug's extra packs
- for i := ancestorIndex + 1; i < len(other.packs); i++ {
+ for i := ancestorIndex + 1; i < len(otherBug.packs); i++ {
// clone is probably not necessary
- newPack := other.packs[i].Clone()
+ newPack := otherBug.packs[i].Clone()
newPacks = append(newPacks, newPack)
bug.lastCommit = newPack.commitHash
diff --git a/bug/interface.go b/bug/interface.go
new file mode 100644
index 00000000..af10b895
--- /dev/null
+++ b/bug/interface.go
@@ -0,0 +1,52 @@
+package bug
+
+import (
+ "github.com/MichaelMure/git-bug/repository"
+)
+
+type Interface interface {
+ // Id return the Bug identifier
+ Id() string
+
+ // HumanId return the Bug identifier truncated for human consumption
+ HumanId() string
+
+ // IsValid check if the Bug data is valid
+ IsValid() bool
+
+ // Append an operation into the staging area, to be committed later
+ Append(op Operation)
+
+ // Append an operation into the staging area, to be committed later
+ HasPendingOp() bool
+
+ // Commit write the staging area in Git and move the operations to the packs
+ Commit(repo repository.Repo) error
+
+ // Merge a different version of the same bug by rebasing operations of this bug
+ // that are not present in the other on top of the chain of operations of the
+ // other version.
+ Merge(repo repository.Repo, other Interface) (bool, error)
+
+ // Lookup for the very first operation of the bug.
+ // For a valid Bug, this operation should be a CreateOp
+ FirstOp() Operation
+
+ // Lookup for the very last operation of the bug.
+ // For a valid Bug, should never be nil
+ LastOp() Operation
+
+ // Compile a bug in a easily usable snapshot
+ Compile() Snapshot
+}
+
+func bugFromInterface(bug Interface) *Bug {
+ switch bug.(type) {
+ case *Bug:
+ return bug.(*Bug)
+ case *WithSnapshot:
+ return bug.(*WithSnapshot).Bug
+ default:
+ panic("missing type case")
+ }
+}
diff --git a/bug/operation_iterator.go b/bug/operation_iterator.go
index 0df8b599..f42b1776 100644
--- a/bug/operation_iterator.go
+++ b/bug/operation_iterator.go
@@ -6,9 +6,9 @@ type OperationIterator struct {
opIndex int
}
-func NewOperationIterator(bug *Bug) *OperationIterator {
+func NewOperationIterator(bug Interface) *OperationIterator {
return &OperationIterator{
- bug: bug,
+ bug: bugFromInterface(bug),
packIndex: 0,
opIndex: -1,
}
diff --git a/bug/operations/add_comment.go b/bug/operations/add_comment.go
index 7ae6f2dd..b4126a8e 100644
--- a/bug/operations/add_comment.go
+++ b/bug/operations/add_comment.go
@@ -42,11 +42,11 @@ func NewAddCommentOp(author bug.Person, message string, files []util.Hash) AddCo
}
// Convenience function to apply the operation
-func Comment(b *bug.Bug, author bug.Person, message string) {
+func Comment(b bug.Interface, author bug.Person, message string) {
CommentWithFiles(b, author, message, nil)
}
-func CommentWithFiles(b *bug.Bug, author bug.Person, message string, files []util.Hash) {
+func CommentWithFiles(b bug.Interface, author bug.Person, message string, files []util.Hash) {
addCommentOp := NewAddCommentOp(author, message, files)
b.Append(addCommentOp)
}
diff --git a/bug/operations/label_change.go b/bug/operations/label_change.go
index 8f608dbc..5d343e5b 100644
--- a/bug/operations/label_change.go
+++ b/bug/operations/label_change.go
@@ -60,7 +60,7 @@ 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.Bug, author bug.Person, add, remove []string) error {
+func ChangeLabels(out io.Writer, b bug.Interface, author bug.Person, add, remove []string) error {
var added, removed []bug.Label
if out == nil {
diff --git a/bug/operations/set_status.go b/bug/operations/set_status.go
index ed6c328c..bafcf5ee 100644
--- a/bug/operations/set_status.go
+++ b/bug/operations/set_status.go
@@ -27,13 +27,13 @@ func NewSetStatusOp(author bug.Person, status bug.Status) SetStatusOperation {
}
// Convenience function to apply the operation
-func Open(b *bug.Bug, author bug.Person) {
+func Open(b bug.Interface, author bug.Person) {
op := NewSetStatusOp(author, bug.OpenStatus)
b.Append(op)
}
// Convenience function to apply the operation
-func Close(b *bug.Bug, author bug.Person) {
+func Close(b bug.Interface, author bug.Person) {
op := NewSetStatusOp(author, bug.ClosedStatus)
b.Append(op)
}
diff --git a/bug/operations/set_title.go b/bug/operations/set_title.go
index 49a270f7..5bd6260a 100644
--- a/bug/operations/set_title.go
+++ b/bug/operations/set_title.go
@@ -29,7 +29,7 @@ func NewSetTitleOp(author bug.Person, title string, was string) SetTitleOperatio
}
// Convenience function to apply the operation
-func SetTitle(b *bug.Bug, author bug.Person, title string) {
+func SetTitle(b bug.Interface, author bug.Person, title string) {
it := bug.NewOperationIterator(b)
var lastTitleOp bug.Operation
diff --git a/bug/with_snapshot.go b/bug/with_snapshot.go
new file mode 100644
index 00000000..0aa3b37d
--- /dev/null
+++ b/bug/with_snapshot.go
@@ -0,0 +1,58 @@
+package bug
+
+import "github.com/MichaelMure/git-bug/repository"
+
+var _ Interface = &WithSnapshot{}
+
+// WithSnapshot encapsulate a Bug and maintain the corresponding Snapshot efficiently
+type WithSnapshot struct {
+ *Bug
+ snap *Snapshot
+}
+
+// Snapshot return the current snapshot
+func (b *WithSnapshot) Snapshot() *Snapshot {
+ if b.snap == nil {
+ snap := b.Bug.Compile()
+ b.snap = &snap
+ }
+ return b.snap
+}
+
+// Append intercept Bug.Append() to update the snapshot efficiently
+func (b *WithSnapshot) Append(op Operation) {
+ b.Bug.Append(op)
+
+ if b.snap == nil {
+ return
+ }
+
+ snap := op.Apply(*b.snap)
+ b.snap = &snap
+}
+
+// Commit intercept Bug.Commit() to update the snapshot efficiently
+func (b *WithSnapshot) Commit(repo repository.Repo) error {
+ err := b.Bug.Commit(repo)
+
+ if err != nil {
+ b.snap = nil
+ return err
+ }
+
+ // Commit() shouldn't change anything of the bug state apart from the
+ // initial ID set
+
+ if b.snap == nil {
+ return nil
+ }
+
+ b.snap.id = b.Bug.id
+ return nil
+}
+
+// Merge intercept Bug.Merge() and clear the snapshot
+func (b *WithSnapshot) Merge(repo repository.Repo, other Interface) (bool, error) {
+ b.snap = nil
+ return b.Bug.Merge(repo, other)
+}