aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bridge/github/export.go2
-rw-r--r--bridge/gitlab/export.go2
-rw-r--r--bridge/jira/export.go2
-rw-r--r--bug/op_create.go2
-rw-r--r--bug/op_create_test.go2
-rw-r--r--bug/operation.go14
-rw-r--r--bug/snapshot.go13
-rw-r--r--cache/bug_excerpt.go21
-rw-r--r--cache/repo_cache.go12
-rw-r--r--commands/json_common.go55
-rw-r--r--commands/ls.go33
-rw-r--r--commands/show.go66
-rw-r--r--commands/user_ls.go33
-rw-r--r--graphql/models/lazy_bug.go8
-rw-r--r--termui/bug_table.go3
-rw-r--r--termui/show_bug.go2
16 files changed, 142 insertions, 128 deletions
diff --git a/bridge/github/export.go b/bridge/github/export.go
index b939d878..57b52ee0 100644
--- a/bridge/github/export.go
+++ b/bridge/github/export.go
@@ -173,7 +173,7 @@ func (ge *githubExporter) ExportAll(ctx context.Context, repo *cache.RepoCache,
// ignore issues created before since date
// TODO: compare the Lamport time instead of using the unix time
- if snapshot.CreatedAt.Before(since) {
+ if snapshot.CreateTime.Before(since) {
out <- core.NewExportNothing(b.Id(), "bug created before the since date")
continue
}
diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go
index 918e6b5e..f32ec0e3 100644
--- a/bridge/gitlab/export.go
+++ b/bridge/gitlab/export.go
@@ -131,7 +131,7 @@ func (ge *gitlabExporter) ExportAll(ctx context.Context, repo *cache.RepoCache,
// ignore issues created before since date
// TODO: compare the Lamport time instead of using the unix time
- if snapshot.CreatedAt.Before(since) {
+ if snapshot.CreateTime.Before(since) {
out <- core.NewExportNothing(b.Id(), "bug created before the since date")
continue
}
diff --git a/bridge/jira/export.go b/bridge/jira/export.go
index 37066263..e6167966 100644
--- a/bridge/jira/export.go
+++ b/bridge/jira/export.go
@@ -165,7 +165,7 @@ func (je *jiraExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, si
// ignore issues whose last modification date is before the query date
// TODO: compare the Lamport time instead of using the unix time
- if snapshot.CreatedAt.Before(since) {
+ if snapshot.CreateTime.Before(since) {
out <- core.NewExportNothing(b.Id(), "bug created before the since date")
continue
}
diff --git a/bug/op_create.go b/bug/op_create.go
index b2af438b..d63d6d2f 100644
--- a/bug/op_create.go
+++ b/bug/op_create.go
@@ -48,7 +48,7 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) {
snapshot.Comments = []Comment{comment}
snapshot.Author = op.Author
- snapshot.CreatedAt = op.Time()
+ snapshot.CreateTime = op.Time()
snapshot.Timeline = []TimelineItem{
&CreateTimelineItem{
diff --git a/bug/op_create_test.go b/bug/op_create_test.go
index ec53b04b..ad9a30fd 100644
--- a/bug/op_create_test.go
+++ b/bug/op_create_test.go
@@ -38,7 +38,7 @@ func TestCreate(t *testing.T) {
Author: rene,
Participants: []identity.Interface{rene},
Actors: []identity.Interface{rene},
- CreatedAt: create.Time(),
+ CreateTime: create.Time(),
Timeline: []TimelineItem{
&CreateTimelineItem{
CommentTimelineItem: NewCommentTimelineItem(id, comment),
diff --git a/bug/operation.go b/bug/operation.go
index 20d44f6c..91df4ef2 100644
--- a/bug/operation.go
+++ b/bug/operation.go
@@ -36,8 +36,6 @@ type Operation interface {
Id() entity.Id
// Time return the time when the operation was added
Time() time.Time
- // GetUnixTime return the unix timestamp when the operation was added
- GetUnixTime() int64
// GetFiles return the files needed by this operation
GetFiles() []git.Hash
// Apply the operation to a Snapshot to create the final state
@@ -89,8 +87,9 @@ func idOperation(op Operation) entity.Id {
type OpBase struct {
OperationType OperationType `json:"type"`
Author identity.Interface `json:"author"`
- UnixTime int64 `json:"timestamp"`
- Metadata map[string]string `json:"metadata,omitempty"`
+ // TODO: part of the data model upgrade, this should eventually be a timestamp + lamport
+ UnixTime int64 `json:"timestamp"`
+ Metadata map[string]string `json:"metadata,omitempty"`
// Not serialized. Store the op's id in memory.
id entity.Id
// Not serialized. Store the extra metadata in memory,
@@ -142,11 +141,6 @@ func (op *OpBase) Time() time.Time {
return time.Unix(op.UnixTime, 0)
}
-// GetUnixTime return the unix timestamp when the operation was added
-func (op *OpBase) GetUnixTime() int64 {
- return op.UnixTime
-}
-
// GetFiles return the files needed by this operation
func (op *OpBase) GetFiles() []git.Hash {
return nil
@@ -158,7 +152,7 @@ func opBaseValidate(op Operation, opType OperationType) error {
return fmt.Errorf("incorrect operation type (expected: %v, actual: %v)", opType, op.base().OperationType)
}
- if op.GetUnixTime() == 0 {
+ if op.Time().Unix() == 0 {
return fmt.Errorf("time not set")
}
diff --git a/bug/snapshot.go b/bug/snapshot.go
index 39366c6d..11df04b2 100644
--- a/bug/snapshot.go
+++ b/bug/snapshot.go
@@ -19,7 +19,7 @@ type Snapshot struct {
Author identity.Interface
Actors []identity.Interface
Participants []identity.Interface
- CreatedAt time.Time
+ CreateTime time.Time
Timeline []TimelineItem
@@ -32,7 +32,7 @@ func (snap *Snapshot) Id() entity.Id {
}
// Return the last time a bug was modified
-func (snap *Snapshot) LastEditTime() time.Time {
+func (snap *Snapshot) EditTime() time.Time {
if len(snap.Operations) == 0 {
return time.Unix(0, 0)
}
@@ -40,15 +40,6 @@ func (snap *Snapshot) LastEditTime() time.Time {
return snap.Operations[len(snap.Operations)-1].Time()
}
-// Return the last timestamp a bug was modified
-func (snap *Snapshot) LastEditUnix() int64 {
- if len(snap.Operations) == 0 {
- return 0
- }
-
- return snap.Operations[len(snap.Operations)-1].GetUnixTime()
-}
-
// GetCreateMetadata return the creation metadata
func (snap *Snapshot) GetCreateMetadata(key string) (string, bool) {
return snap.Operations[0].GetMetadata(key)
diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go
index 36c7dcfe..e9d5863f 100644
--- a/cache/bug_excerpt.go
+++ b/cache/bug_excerpt.go
@@ -3,6 +3,7 @@ package cache
import (
"encoding/gob"
"fmt"
+ "time"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/entity"
@@ -22,8 +23,8 @@ type BugExcerpt struct {
CreateLamportTime lamport.Time
EditLamportTime lamport.Time
- CreateUnixTime int64
- EditUnixTime int64
+ createUnixTime int64
+ editUnixTime int64
Status bug.Status
Labels []bug.Label
@@ -79,8 +80,8 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
Id: b.Id(),
CreateLamportTime: b.CreateLamportTime(),
EditLamportTime: b.EditLamportTime(),
- CreateUnixTime: b.FirstOp().GetUnixTime(),
- EditUnixTime: snap.LastEditUnix(),
+ createUnixTime: b.FirstOp().Time().Unix(),
+ editUnixTime: snap.EditTime().Unix(),
Status: snap.Status,
Labels: snap.Labels,
Actors: actorsIds,
@@ -105,6 +106,14 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
return e
}
+func (b *BugExcerpt) CreateTime() time.Time {
+ return time.Unix(b.createUnixTime, 0)
+}
+
+func (b *BugExcerpt) EditTime() time.Time {
+ return time.Unix(b.editUnixTime, 0)
+}
+
/*
* Sorting
*/
@@ -144,7 +153,7 @@ func (b BugsByCreationTime) Less(i, j int) bool {
// by the first sorting using the logical clock. That means that if users
// synchronize their bugs regularly, the timestamp will rarely be used, and
// should still provide a kinda accurate sorting when needed.
- return b[i].CreateUnixTime < b[j].CreateUnixTime
+ return b[i].createUnixTime < b[j].createUnixTime
}
func (b BugsByCreationTime) Swap(i, j int) {
@@ -172,7 +181,7 @@ func (b BugsByEditTime) Less(i, j int) bool {
// by the first sorting using the logical clock. That means that if users
// synchronize their bugs regularly, the timestamp will rarely be used, and
// should still provide a kinda accurate sorting when needed.
- return b[i].EditUnixTime < b[j].EditUnixTime
+ return b[i].editUnixTime < b[j].editUnixTime
}
func (b BugsByEditTime) Swap(i, j int) {
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index f2e1c7d0..83ce4802 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -29,7 +29,8 @@ const identityCacheFile = "identity-cache"
// 1: original format
// 2: added cache for identities with a reference in the bug cache
-const formatVersion = 2
+// 3: CreateUnixTime --> createUnixTime, EditUnixTime --> editUnixTime
+const formatVersion = 3
type ErrInvalidCacheFormat struct {
message string
@@ -99,10 +100,13 @@ func NewNamedRepoCache(r repository.ClockedRepo, name string) (*RepoCache, error
if err == nil {
return c, nil
}
- if _, ok := err.(ErrInvalidCacheFormat); ok {
+ if _, ok := err.(ErrInvalidCacheFormat); !ok {
+ // Actual error
return nil, err
}
+ // We have an outdated cache format, rebuilding it.
+
err = c.buildCache()
if err != nil {
return nil, err
@@ -254,7 +258,7 @@ func (c *RepoCache) loadBugCache() error {
return err
}
- if aux.Version != 2 {
+ if aux.Version != formatVersion {
return ErrInvalidCacheFormat{
message: fmt.Sprintf("unknown cache format version %v", aux.Version),
}
@@ -286,7 +290,7 @@ func (c *RepoCache) loadIdentityCache() error {
return err
}
- if aux.Version != 2 {
+ if aux.Version != formatVersion {
return ErrInvalidCacheFormat{
message: fmt.Sprintf("unknown cache format version %v", aux.Version),
}
diff --git a/commands/json_common.go b/commands/json_common.go
new file mode 100644
index 00000000..9a144a1e
--- /dev/null
+++ b/commands/json_common.go
@@ -0,0 +1,55 @@
+package commands
+
+import (
+ "time"
+
+ "github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/identity"
+ "github.com/MichaelMure/git-bug/util/lamport"
+)
+
+type JSONIdentity struct {
+ Id string `json:"id"`
+ HumanId string `json:"human_id"`
+ Name string `json:"name"`
+ Login string `json:"login"`
+}
+
+func NewJSONIdentity(i identity.Interface) JSONIdentity {
+ return JSONIdentity{
+ Id: i.Id().String(),
+ HumanId: i.Id().Human(),
+ Name: i.Name(),
+ Login: i.Login(),
+ }
+}
+
+func NewJSONIdentityFromExcerpt(excerpt *cache.IdentityExcerpt) JSONIdentity {
+ return JSONIdentity{
+ Id: excerpt.Id.String(),
+ HumanId: excerpt.Id.Human(),
+ Name: excerpt.Name,
+ Login: excerpt.Login,
+ }
+}
+
+func NewJSONIdentityFromLegacyExcerpt(excerpt *cache.LegacyAuthorExcerpt) JSONIdentity {
+ return JSONIdentity{
+ Name: excerpt.Name,
+ Login: excerpt.Login,
+ }
+}
+
+type JSONTime struct {
+ Timestamp int64 `json:"timestamp"`
+ Time time.Time `json:"time"`
+ Lamport lamport.Time `json:"lamport,omitempty"`
+}
+
+func NewJSONTime(t time.Time, l lamport.Time) JSONTime {
+ return JSONTime{
+ Timestamp: t.Unix(),
+ Time: t,
+ Lamport: l,
+ }
+}
diff --git a/commands/ls.go b/commands/ls.go
index e1a7166a..34f1f982 100644
--- a/commands/ls.go
+++ b/commands/ls.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"strings"
- "time"
text "github.com/MichaelMure/go-term-text"
"github.com/spf13/cobra"
@@ -75,10 +74,10 @@ func runLsBug(_ *cobra.Command, args []string) error {
}
type JSONBugExcerpt struct {
- Id string `json:"id"`
- HumanId string `json:"human_id"`
- CreationTime time.Time `json:"creation_time"`
- LastEdited time.Time `json:"last_edited"`
+ Id string `json:"id"`
+ HumanId string `json:"human_id"`
+ CreateTime JSONTime `json:"create_time"`
+ EditTime JSONTime `json:"edit_time"`
Status string `json:"status"`
Labels []bug.Label `json:"labels"`
@@ -95,15 +94,15 @@ func lsJsonFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt)
jsonBugs := make([]JSONBugExcerpt, len(bugExcerpts))
for i, b := range bugExcerpts {
jsonBug := JSONBugExcerpt{
- Id: b.Id.String(),
- HumanId: b.Id.Human(),
- CreationTime: time.Unix(b.CreateUnixTime, 0),
- LastEdited: time.Unix(b.EditUnixTime, 0),
- Status: b.Status.String(),
- Labels: b.Labels,
- Title: b.Title,
- Comments: b.LenComments,
- Metadata: b.CreateMetadata,
+ Id: b.Id.String(),
+ HumanId: b.Id.Human(),
+ CreateTime: NewJSONTime(b.CreateTime(), b.CreateLamportTime),
+ EditTime: NewJSONTime(b.EditTime(), b.EditLamportTime),
+ Status: b.Status.String(),
+ Labels: b.Labels,
+ Title: b.Title,
+ Comments: b.LenComments,
+ Metadata: b.CreateMetadata,
}
if b.AuthorId != "" {
@@ -225,15 +224,13 @@ func lsOrgmodeFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerp
fmt.Printf("* %s %s [%s] %s: %s %s\n",
b.Id.Human(),
status,
- time.Unix(b.CreateUnixTime, 0),
+ b.CreateTime(),
name,
title,
labelsString,
)
- fmt.Printf("** Last Edited: %s\n",
- time.Unix(b.EditUnixTime, 0).String(),
- )
+ fmt.Printf("** Last Edited: %s\n", b.EditTime().String())
fmt.Printf("** Actors:\n")
for _, element := range b.Actors {
diff --git a/commands/show.go b/commands/show.go
index 4915f181..2f4e46ed 100644
--- a/commands/show.go
+++ b/commands/show.go
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"strings"
- "time"
"github.com/spf13/cobra"
@@ -47,9 +46,9 @@ func runShowBug(_ *cobra.Command, args []string) error {
case "authorEmail":
fmt.Printf("%s\n", snapshot.Author.Email())
case "createTime":
- fmt.Printf("%s\n", snapshot.CreatedAt.String())
+ fmt.Printf("%s\n", snapshot.CreateTime.String())
case "lastEdit":
- fmt.Printf("%s\n", snapshot.LastEditTime().String())
+ fmt.Printf("%s\n", snapshot.EditTime().String())
case "humanId":
fmt.Printf("%s\n", snapshot.Id().Human())
case "id":
@@ -101,11 +100,11 @@ func showDefaultFormatter(snapshot *bug.Snapshot) error {
fmt.Printf("%s opened this issue %s\n",
colors.Magenta(snapshot.Author.DisplayName()),
- snapshot.CreatedAt.String(),
+ snapshot.CreateTime.String(),
)
fmt.Printf("This was last edited at %s\n\n",
- snapshot.LastEditTime().String(),
+ snapshot.EditTime().String(),
)
// Labels
@@ -166,37 +165,45 @@ func showDefaultFormatter(snapshot *bug.Snapshot) error {
}
type JSONBugSnapshot struct {
- Id string `json:"id"`
- HumanId string `json:"human_id"`
- CreationTime time.Time `json:"creation_time"`
- LastEdited time.Time `json:"last_edited"`
-
+ Id string `json:"id"`
+ HumanId string `json:"human_id"`
+ CreateTime JSONTime `json:"create_time"`
+ EditTime JSONTime `json:"edit_time"`
Status string `json:"status"`
Labels []bug.Label `json:"labels"`
Title string `json:"title"`
Author JSONIdentity `json:"author"`
Actors []JSONIdentity `json:"actors"`
Participants []JSONIdentity `json:"participants"`
-
- Comments []JSONComment `json:"comments"`
+ Comments []JSONComment `json:"comments"`
}
type JSONComment struct {
- Id int `json:"id"`
+ Id string `json:"id"`
+ HumanId string `json:"human_id"`
Author JSONIdentity `json:"author"`
Message string `json:"message"`
}
+func NewJSONComment(comment bug.Comment) JSONComment {
+ return JSONComment{
+ Id: comment.Id().String(),
+ HumanId: comment.Id().Human(),
+ Author: NewJSONIdentity(comment.Author),
+ Message: comment.Message,
+ }
+}
+
func showJsonFormatter(snapshot *bug.Snapshot) error {
jsonBug := JSONBugSnapshot{
- Id: snapshot.Id().String(),
- HumanId: snapshot.Id().Human(),
- CreationTime: snapshot.CreatedAt,
- LastEdited: snapshot.LastEditTime(),
- Status: snapshot.Status.String(),
- Labels: snapshot.Labels,
- Title: snapshot.Title,
- Author: NewJSONIdentity(snapshot.Author),
+ Id: snapshot.Id().String(),
+ HumanId: snapshot.Id().Human(),
+ CreateTime: NewJSONTime(snapshot.CreateTime, 0),
+ EditTime: NewJSONTime(snapshot.EditTime(), 0),
+ Status: snapshot.Status.String(),
+ Labels: snapshot.Labels,
+ Title: snapshot.Title,
+ Author: NewJSONIdentity(snapshot.Author),
}
jsonBug.Actors = make([]JSONIdentity, len(snapshot.Actors))
@@ -209,18 +216,9 @@ func showJsonFormatter(snapshot *bug.Snapshot) error {
jsonBug.Participants[i] = NewJSONIdentity(element)
}
+ jsonBug.Comments = make([]JSONComment, len(snapshot.Comments))
for i, comment := range snapshot.Comments {
- var message string
- if comment.Message == "" {
- message = "No description provided."
- } else {
- message = comment.Message
- }
- jsonBug.Comments = append(jsonBug.Comments, JSONComment{
- Id: i,
- Author: NewJSONIdentity(comment.Author),
- Message: message,
- })
+ jsonBug.Comments[i] = NewJSONComment(comment)
}
jsonObject, _ := json.MarshalIndent(jsonBug, "", " ")
@@ -242,11 +240,11 @@ func showOrgmodeFormatter(snapshot *bug.Snapshot) error {
)
fmt.Printf("* Creation Time: %s\n",
- snapshot.CreatedAt.String(),
+ snapshot.CreateTime.String(),
)
fmt.Printf("* Last Edit: %s\n",
- snapshot.LastEditTime().String(),
+ snapshot.EditTime().String(),
)
// Labels
diff --git a/commands/user_ls.go b/commands/user_ls.go
index 78ef8258..b3fb32e6 100644
--- a/commands/user_ls.go
+++ b/commands/user_ls.go
@@ -7,7 +7,6 @@ import (
"github.com/spf13/cobra"
"github.com/MichaelMure/git-bug/cache"
- "github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/colors"
"github.com/MichaelMure/git-bug/util/interrupt"
)
@@ -44,38 +43,6 @@ func runUserLs(_ *cobra.Command, _ []string) error {
}
}
-type JSONIdentity struct {
- Id string `json:"id"`
- HumanId string `json:"human_id"`
- Name string `json:"name"`
- Login string `json:"login"`
-}
-
-func NewJSONIdentity(i identity.Interface) JSONIdentity {
- return JSONIdentity{
- Id: i.Id().String(),
- HumanId: i.Id().Human(),
- Name: i.Name(),
- Login: i.Login(),
- }
-}
-
-func NewJSONIdentityFromExcerpt(excerpt *cache.IdentityExcerpt) JSONIdentity {
- return JSONIdentity{
- Id: excerpt.Id.String(),
- HumanId: excerpt.Id.Human(),
- Name: excerpt.Name,
- Login: excerpt.Login,
- }
-}
-
-func NewJSONIdentityFromLegacyExcerpt(excerpt *cache.LegacyAuthorExcerpt) JSONIdentity {
- return JSONIdentity{
- Name: excerpt.Name,
- Login: excerpt.Login,
- }
-}
-
func userLsDefaultFormatter(users []*cache.IdentityExcerpt) error {
for _, user := range users {
fmt.Printf("%s %s\n",
diff --git a/graphql/models/lazy_bug.go b/graphql/models/lazy_bug.go
index 6034e80d..a7840df2 100644
--- a/graphql/models/lazy_bug.go
+++ b/graphql/models/lazy_bug.go
@@ -81,7 +81,7 @@ func (lb *lazyBug) Id() entity.Id {
}
func (lb *lazyBug) LastEdit() time.Time {
- return time.Unix(lb.excerpt.EditUnixTime, 0)
+ return lb.excerpt.EditTime()
}
func (lb *lazyBug) Status() bug.Status {
@@ -133,7 +133,7 @@ func (lb *lazyBug) Participants() ([]IdentityWrapper, error) {
}
func (lb *lazyBug) CreatedAt() time.Time {
- return time.Unix(lb.excerpt.CreateUnixTime, 0)
+ return lb.excerpt.CreateTime()
}
func (lb *lazyBug) Timeline() ([]bug.TimelineItem, error) {
@@ -163,7 +163,7 @@ func NewLoadedBug(snap *bug.Snapshot) *loadedBug {
}
func (l *loadedBug) LastEdit() time.Time {
- return l.Snapshot.LastEditTime()
+ return l.Snapshot.EditTime()
}
func (l *loadedBug) Status() bug.Status {
@@ -203,7 +203,7 @@ func (l *loadedBug) Participants() ([]IdentityWrapper, error) {
}
func (l *loadedBug) CreatedAt() time.Time {
- return l.Snapshot.CreatedAt
+ return l.Snapshot.CreateTime
}
func (l *loadedBug) Timeline() ([]bug.TimelineItem, error) {
diff --git a/termui/bug_table.go b/termui/bug_table.go
index 2913ac80..e57a2b15 100644
--- a/termui/bug_table.go
+++ b/termui/bug_table.go
@@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"strings"
- "time"
"github.com/MichaelMure/go-term-text"
"github.com/awesome-gocui/gocui"
@@ -315,7 +314,7 @@ func (bt *bugTable) render(v *gocui.View, maxX int) {
authorDisplayName = excerpt.LegacyAuthor.DisplayName()
}
- lastEditTime := time.Unix(excerpt.EditUnixTime, 0)
+ lastEditTime := excerpt.EditTime()
id := text.LeftPadMaxLine(excerpt.Id.Human(), columnWidths["id"], 1)
status := text.LeftPadMaxLine(excerpt.Status.String(), columnWidths["status"], 1)
diff --git a/termui/show_bug.go b/termui/show_bug.go
index e483117a..a5da519f 100644
--- a/termui/show_bug.go
+++ b/termui/show_bug.go
@@ -219,7 +219,7 @@ func (sb *showBug) renderMain(g *gocui.Gui, mainView *gocui.View) error {
colors.Bold(snap.Title),
colors.Yellow(snap.Status),
colors.Magenta(snap.Author.DisplayName()),
- snap.CreatedAt.Format(timeLayout),
+ snap.CreateTime.Format(timeLayout),
edited,
)
bugHeader, lines := text.Wrap(bugHeader, maxX)