1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
package bug
import (
"fmt"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util"
"github.com/kevinburke/go.uuid"
)
const BugsRefPattern = "refs/bugs/"
// Bug hold the data of a bug thread, organized in a way close to
// how it will be persisted inside Git. This is the datastructure
// used for merge of two different version.
type Bug struct {
// Id used as unique identifier
id uuid.UUID
lastCommit util.Hash
// TODO: need a way to order bugs, probably a Lamport clock
packs []OperationPack
staging OperationPack
}
// Create a new Bug
func NewBug() (*Bug, error) {
// Creating UUID Version 4
id, err := uuid.ID4()
if err != nil {
return nil, err
}
return &Bug{
id: id,
lastCommit: "",
}, nil
}
// IsValid check if the Bug data is valid
func (bug *Bug) IsValid() bool {
// non-empty
if len(bug.packs) == 0 && bug.staging.IsEmpty() {
return false
}
// check if each pack is valid
for _, pack := range bug.packs {
if !pack.IsValid() {
return false
}
}
// check if staging is valid if needed
if !bug.staging.IsEmpty() {
if !bug.staging.IsValid() {
return false
}
}
// The very first Op should be a CREATE
firstOp := bug.firstOp()
if firstOp == nil || firstOp.OpType() != CREATE {
return false
}
// Check that there is no more CREATE op
it := NewOperationIterator(bug)
createCount := 0
for it.Next() {
if it.Value().OpType() == CREATE {
createCount++
}
}
if createCount != 1 {
return false
}
return true
}
func (bug *Bug) Append(op Operation) {
bug.staging.Append(op)
}
// Write the staging area in Git move the operations to the packs
func (bug *Bug) Commit(repo repository.Repo) error {
if bug.staging.IsEmpty() {
return nil
}
// Write the Ops as a Git blob containing the serialized array
hash, err := bug.staging.Write(repo)
if err != nil {
return err
}
// Write a Git tree referencing this blob
hash, err = repo.StoreTree(map[string]util.Hash{
"ops": hash,
})
if err != nil {
return err
}
// Write a Git commit referencing the tree, with the previous commit as parent
if bug.lastCommit != "" {
hash, err = repo.StoreCommitWithParent(hash, bug.lastCommit)
} else {
hash, err = repo.StoreCommit(hash)
}
if err != nil {
return err
}
// Create or update the Git reference for this bug
ref := fmt.Sprintf("%s%s", BugsRefPattern, bug.id.String())
err = repo.UpdateRef(ref, hash)
if err != nil {
return err
}
bug.packs = append(bug.packs, bug.staging)
bug.staging = OperationPack{}
return nil
}
func (bug *Bug) HumanId() string {
return bug.id.String()
}
func (bug *Bug) firstOp() Operation {
for _, pack := range bug.packs {
for _, op := range pack.Operations {
return op
}
}
if !bug.staging.IsEmpty() {
return bug.staging.Operations[0]
}
return nil
}
|