diff options
Diffstat (limited to 'bug')
-rw-r--r-- | bug/bug.go | 20 | ||||
-rw-r--r-- | bug/interface.go | 52 | ||||
-rw-r--r-- | bug/operation_iterator.go | 4 | ||||
-rw-r--r-- | bug/operations/add_comment.go | 4 | ||||
-rw-r--r-- | bug/operations/label_change.go | 2 | ||||
-rw-r--r-- | bug/operations/set_status.go | 4 | ||||
-rw-r--r-- | bug/operations/set_title.go | 2 | ||||
-rw-r--r-- | bug/with_snapshot.go | 58 |
8 files changed, 130 insertions, 16 deletions
@@ -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) +} |