aboutsummaryrefslogtreecommitdiffstats
path: root/bug
diff options
context:
space:
mode:
Diffstat (limited to 'bug')
-rw-r--r--bug/comment.go46
-rw-r--r--bug/comment_test.go27
-rw-r--r--bug/op_add_comment.go5
-rw-r--r--bug/op_create.go5
-rw-r--r--bug/snapshot.go5
5 files changed, 84 insertions, 4 deletions
diff --git a/bug/comment.go b/bug/comment.go
index 4c9d118e..1a9ca05a 100644
--- a/bug/comment.go
+++ b/bug/comment.go
@@ -1,6 +1,8 @@
package bug
import (
+ "strings"
+
"github.com/dustin/go-humanize"
"github.com/MichaelMure/git-bug/entity"
@@ -31,6 +33,50 @@ func (c Comment) Id() entity.Id {
return c.id
}
+const compiledCommentIdFormat = "BCBCBCBBBCBBBBCBBBBCBBBBCBBBBCBBBBCBBBBC"
+
+// DeriveCommentId compute a merged Id for a comment holding information from
+// both the Bug's Id and the Comment's Id. This allow to later find efficiently
+// a Comment because we can access the bug directly instead of searching for a
+// Bug that has a Comment matching the Id.
+//
+// To allow the use of an arbitrary length prefix of this merged Id, Ids from Bug
+// and Comment are interleaved with this irregular pattern to give the best chance
+// to find the Comment even with a 7 character prefix.
+//
+// A complete merged Id hold 30 characters for the Bug and 10 for the Comment,
+// which give a key space of 36^30 for the Bug (~5 * 10^46) and 36^10 for the
+// Comment (~3 * 10^15). This asymmetry assume a reasonable number of Comment
+// within a Bug, while still allowing for a vast key space for Bug (that is, a
+// globally merged bug database) with a low risk of collision.
+func DeriveCommentId(bugId entity.Id, commentId entity.Id) entity.Id {
+ var id strings.Builder
+ for _, char := range compiledCommentIdFormat {
+ if char == 'B' {
+ id.WriteByte(bugId[0])
+ bugId = bugId[1:]
+ } else {
+ id.WriteByte(commentId[0])
+ commentId = commentId[1:]
+ }
+ }
+ return entity.Id(id.String())
+}
+
+func SplitCommentId(prefix string) (bugPrefix string, commentPrefix string) {
+ var bugIdPrefix strings.Builder
+ var commentIdPrefix strings.Builder
+
+ for i, char := range prefix {
+ if compiledCommentIdFormat[i] == 'B' {
+ bugIdPrefix.WriteRune(char)
+ } else {
+ commentIdPrefix.WriteRune(char)
+ }
+ }
+ return bugIdPrefix.String(), commentIdPrefix.String()
+}
+
// FormatTimeRel format the UnixTime of the comment for human consumption
func (c Comment) FormatTimeRel() string {
return humanize.Time(c.UnixTime.Time())
diff --git a/bug/comment_test.go b/bug/comment_test.go
new file mode 100644
index 00000000..423d10d8
--- /dev/null
+++ b/bug/comment_test.go
@@ -0,0 +1,27 @@
+package bug
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/MichaelMure/git-bug/entity"
+)
+
+func TestCommentId(t *testing.T) {
+ bugId := entity.Id("abcdefghijklmnopqrstuvwxyz1234__________")
+ opId := entity.Id("ABCDEFGHIJ______________________________")
+ expectedId := entity.Id("aAbBcCdefDghijEklmnFopqrGstuvHwxyzI1234J")
+
+ mergedId := DeriveCommentId(bugId, opId)
+ require.Equal(t, expectedId, mergedId)
+
+ // full length
+ splitBugId, splitCommentId := SplitCommentId(mergedId.String())
+ require.Equal(t, string(bugId[:30]), splitBugId)
+ require.Equal(t, string(opId[:10]), splitCommentId)
+
+ splitBugId, splitCommentId = SplitCommentId(string(expectedId[:6]))
+ require.Equal(t, string(bugId[:3]), splitBugId)
+ require.Equal(t, string(opId[:3]), splitCommentId)
+}
diff --git a/bug/op_add_comment.go b/bug/op_add_comment.go
index 3f19e42e..df426ee0 100644
--- a/bug/op_add_comment.go
+++ b/bug/op_add_comment.go
@@ -36,8 +36,9 @@ func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
snapshot.addActor(op.Author)
snapshot.addParticipant(op.Author)
+ commentId := DeriveCommentId(snapshot.Id(), op.Id())
comment := Comment{
- id: op.Id(),
+ id: commentId,
Message: op.Message,
Author: op.Author,
Files: op.Files,
@@ -47,7 +48,7 @@ func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
snapshot.Comments = append(snapshot.Comments, comment)
item := &AddCommentTimelineItem{
- CommentTimelineItem: NewCommentTimelineItem(op.Id(), comment),
+ CommentTimelineItem: NewCommentTimelineItem(commentId, comment),
}
snapshot.Timeline = append(snapshot.Timeline, item)
diff --git a/bug/op_create.go b/bug/op_create.go
index 41e0fca1..15fb69b5 100644
--- a/bug/op_create.go
+++ b/bug/op_create.go
@@ -59,8 +59,9 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) {
snapshot.Title = op.Title
+ commentId := DeriveCommentId(snapshot.Id(), op.Id())
comment := Comment{
- id: op.Id(),
+ id: commentId,
Message: op.Message,
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
@@ -72,7 +73,7 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) {
snapshot.Timeline = []TimelineItem{
&CreateTimelineItem{
- CommentTimelineItem: NewCommentTimelineItem(op.Id(), comment),
+ CommentTimelineItem: NewCommentTimelineItem(commentId, comment),
},
}
}
diff --git a/bug/snapshot.go b/bug/snapshot.go
index 11df04b2..0005b930 100644
--- a/bug/snapshot.go
+++ b/bug/snapshot.go
@@ -28,6 +28,11 @@ type Snapshot struct {
// Return the Bug identifier
func (snap *Snapshot) Id() entity.Id {
+ if snap.id == "" {
+ // simply panic as it would be a coding error
+ // (using an id of a bug not stored yet)
+ panic("no id yet")
+ }
return snap.id
}