aboutsummaryrefslogtreecommitdiffstats
path: root/bug
diff options
context:
space:
mode:
Diffstat (limited to 'bug')
-rw-r--r--bug/bug.go107
-rw-r--r--bug/operation_pack.go18
2 files changed, 118 insertions, 7 deletions
diff --git a/bug/bug.go b/bug/bug.go
index a82ee371..c5268cc2 100644
--- a/bug/bug.go
+++ b/bug/bug.go
@@ -56,7 +56,7 @@ func NewBug() (*Bug, error) {
// Find an existing Bug matching a prefix
func FindBug(repo repository.Repo, prefix string) (*Bug, error) {
- refs, err := repo.ListRefs(BugsRefPattern)
+ ids, err := repo.ListRefs(BugsRefPattern)
if err != nil {
return nil, err
@@ -65,9 +65,9 @@ func FindBug(repo repository.Repo, prefix string) (*Bug, error) {
// preallocate but empty
matching := make([]string, 0, 5)
- for _, ref := range refs {
- if strings.HasPrefix(ref, prefix) {
- matching = append(matching, ref)
+ for _, id := range ids {
+ if strings.HasPrefix(id, prefix) {
+ matching = append(matching, id)
}
}
@@ -79,21 +79,25 @@ func FindBug(repo repository.Repo, prefix string) (*Bug, error) {
return nil, fmt.Errorf("Multiple matching bug found:\n%s", strings.Join(matching, "\n"))
}
- return ReadBug(repo, matching[0])
+ return ReadBug(repo, BugsRefPattern+matching[0])
}
// Read and parse a Bug from git
-func ReadBug(repo repository.Repo, id string) (*Bug, error) {
- hashes, err := repo.ListCommits(BugsRefPattern + id)
+func ReadBug(repo repository.Repo, ref string) (*Bug, error) {
+ hashes, err := repo.ListCommits(ref)
if err != nil {
return nil, err
}
+ refSplitted := strings.Split(ref, "/")
+ id := refSplitted[len(refSplitted)-1]
+
bug := Bug{
id: id,
}
+ // Load each OperationPack
for _, hash := range hashes {
entries, err := repo.ListEntries(hash)
@@ -144,6 +148,13 @@ func ReadBug(repo repository.Repo, id string) (*Bug, error) {
return nil, err
}
+ // tag the pack with the commit hash
+ op.commitHash = hash
+
+ if err != nil {
+ return nil, err
+ }
+
bug.packs = append(bug.packs, *op)
}
@@ -251,14 +262,96 @@ func (bug *Bug) Commit(repo repository.Repo) error {
return nil
}
+// 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) {
+
+ if bug.id != other.id {
+ return false, errors.New("merging unrelated bugs is not supported")
+ }
+
+ if len(other.staging.Operations) > 0 {
+ return false, errors.New("merging a bug with a non-empty staging is not supported")
+ }
+
+ if bug.lastCommit == "" || other.lastCommit == "" {
+ return false, errors.New("can't merge a bug that has never been stored")
+ }
+
+ ancestor, err := repo.FindCommonAncestor(bug.lastCommit, other.lastCommit)
+
+ if err != nil {
+ return false, err
+ }
+
+ rebaseStarted := false
+ updated := false
+
+ for i, pack := range bug.packs {
+ if pack.commitHash == ancestor {
+ rebaseStarted = true
+
+ // get other bug's extra pack
+ for j := i + 1; j < len(other.packs); j++ {
+ // clone is probably not necessary
+ newPack := other.packs[j].Clone()
+
+ bug.packs = append(bug.packs, newPack)
+ bug.lastCommit = newPack.commitHash
+ updated = true
+ }
+
+ continue
+ }
+
+ if !rebaseStarted {
+ continue
+ }
+
+ updated = true
+
+ // get the referenced git tree
+ treeHash, err := repo.GetTreeHash(pack.commitHash)
+
+ if err != nil {
+ return false, err
+ }
+
+ // create a new commit with the correct ancestor
+ hash, err := repo.StoreCommitWithParent(treeHash, bug.lastCommit)
+
+ // replace the pack
+ bug.packs[i] = pack.Clone()
+ bug.packs[i].commitHash = hash
+
+ // update the bug
+ bug.lastCommit = hash
+ }
+
+ // Update the git ref
+ if updated {
+ err := repo.UpdateRef(BugsRefPattern+bug.id, bug.lastCommit)
+ if err != nil {
+ return false, err
+ }
+ }
+
+ return updated, nil
+}
+
+// Return the Bug identifier
func (bug *Bug) Id() string {
return bug.id
}
+// Return the Bug identifier truncated for human consumption
func (bug *Bug) HumanId() string {
return fmt.Sprintf("%.8s", bug.id)
}
+// Lookup for the very first operation of the bug.
+// For a valid Bug, this operation should be a CREATE
func (bug *Bug) firstOp() Operation {
for _, pack := range bug.packs {
for _, op := range pack.Operations {
diff --git a/bug/operation_pack.go b/bug/operation_pack.go
index 60016474..cce1845c 100644
--- a/bug/operation_pack.go
+++ b/bug/operation_pack.go
@@ -15,6 +15,9 @@ import (
// apply to get the final state of the Bug
type OperationPack struct {
Operations []Operation
+
+ // Private field so not serialized by gob
+ commitHash util.Hash
}
func ParseOperationPack(data []byte) (*OperationPack, error) {
@@ -73,3 +76,18 @@ func (opp *OperationPack) Write(repo repository.Repo) (util.Hash, error) {
return hash, nil
}
+
+// Make a deep copy
+func (opp *OperationPack) Clone() OperationPack {
+
+ clone := OperationPack{
+ Operations: make([]Operation, len(opp.Operations)),
+ commitHash: opp.commitHash,
+ }
+
+ for i, op := range opp.Operations {
+ clone.Operations[i] = op
+ }
+
+ return clone
+}