aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2020-06-26 01:02:27 +0200
committerGitHub <noreply@github.com>2020-06-26 01:02:27 +0200
commit2dd0dbb1344ae9293aae05346f977b5d5907934b (patch)
tree610ff7e381ef99827dc1b1dbc9ae4ea137fe5e21
parentf790083fb28ac0e68acddc9d5e7a709107a70bab (diff)
parentc326007d01b623bcf883f74f31fc3e5ab3078396 (diff)
downloadgit-bug-2dd0dbb1344ae9293aae05346f977b5d5907934b.tar.gz
Merge pull request #410 from MichaelMure/output-formatting-2
Add formatting options to the 'show' and 'user ls' commands
-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.go27
-rw-r--r--commands/json_common.go55
-rw-r--r--commands/ls.go152
-rw-r--r--commands/show.go198
-rw-r--r--commands/user_ls.go47
-rw-r--r--doc/man/git-bug-ls.12
-rw-r--r--doc/man/git-bug-show.18
-rw-r--r--doc/man/git-bug-user-ls.14
-rw-r--r--doc/md/git-bug_ls.md2
-rw-r--r--doc/md/git-bug_show.md5
-rw-r--r--doc/md/git-bug_user_ls.md3
-rw-r--r--graphql/models/lazy_bug.go8
-rw-r--r--misc/bash_completion/git-bug9
-rw-r--r--misc/powershell_completion/git-bug13
-rw-r--r--misc/zsh_completion/git-bug8
-rw-r--r--termui/bug_table.go3
-rw-r--r--termui/show_bug.go2
25 files changed, 457 insertions, 147 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..e70904df 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -29,15 +29,8 @@ const identityCacheFile = "identity-cache"
// 1: original format
// 2: added cache for identities with a reference in the bug cache
-const formatVersion = 2
-
-type ErrInvalidCacheFormat struct {
- message string
-}
-
-func (e ErrInvalidCacheFormat) Error() string {
- return e.message
-}
+// 3: CreateUnixTime --> createUnixTime, EditUnixTime --> editUnixTime
+const formatVersion = 3
var _ repository.RepoCommon = &RepoCache{}
@@ -99,10 +92,8 @@ func NewNamedRepoCache(r repository.ClockedRepo, name string) (*RepoCache, error
if err == nil {
return c, nil
}
- if _, ok := err.(ErrInvalidCacheFormat); ok {
- return nil, err
- }
+ // Cache is either missing, broken or outdated. Rebuilding.
err = c.buildCache()
if err != nil {
return nil, err
@@ -254,10 +245,8 @@ func (c *RepoCache) loadBugCache() error {
return err
}
- if aux.Version != 2 {
- return ErrInvalidCacheFormat{
- message: fmt.Sprintf("unknown cache format version %v", aux.Version),
- }
+ if aux.Version != formatVersion {
+ return fmt.Errorf("unknown cache format version %v", aux.Version)
}
c.bugExcerpts = aux.Excerpts
@@ -286,10 +275,8 @@ func (c *RepoCache) loadIdentityCache() error {
return err
}
- if aux.Version != 2 {
- return ErrInvalidCacheFormat{
- message: fmt.Sprintf("unknown cache format version %v", aux.Version),
- }
+ if aux.Version != formatVersion {
+ return fmt.Errorf("unknown cache format version %v", aux.Version)
}
c.identitiesExcerpts = aux.Excerpts
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 68cb0cfc..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"
@@ -61,6 +60,8 @@ func runLsBug(_ *cobra.Command, args []string) error {
}
switch lsOutputFormat {
+ case "org-mode":
+ return lsOrgmodeFormatter(backend, bugExcerpt)
case "plain":
return lsPlainFormatter(backend, bugExcerpt)
case "json":
@@ -72,11 +73,11 @@ func runLsBug(_ *cobra.Command, args []string) error {
}
}
-type JSONBug struct {
- Id string `json:"id"`
- HumanId string `json:"human_id"`
- CreationTime time.Time `json:"creation_time"`
- LastEdited time.Time `json:"last_edited"`
+type JSONBugExcerpt struct {
+ 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"`
@@ -89,29 +90,19 @@ type JSONBug struct {
Metadata map[string]string `json:"metadata"`
}
-type JSONIdentity struct {
- Id string `json:"id"`
- HumanId string `json:"human_id"`
- Name string `json:"name"`
- Login string `json:"login"`
-}
-
func lsJsonFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error {
- jsonBugs := make([]JSONBug, len(bugExcerpts))
+ jsonBugs := make([]JSONBugExcerpt, len(bugExcerpts))
for i, b := range bugExcerpts {
- jsonBug := JSONBug{
- b.Id.String(),
- b.Id.Human(),
- time.Unix(b.CreateUnixTime, 0),
- time.Unix(b.EditUnixTime, 0),
- b.Status.String(),
- b.Labels,
- b.Title,
- []JSONIdentity{},
- []JSONIdentity{},
- JSONIdentity{},
- b.LenComments,
- b.CreateMetadata,
+ jsonBug := JSONBugExcerpt{
+ 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 != "" {
@@ -119,41 +110,27 @@ func lsJsonFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt)
if err != nil {
return err
}
-
- jsonBug.Author.Name = author.DisplayName()
- jsonBug.Author.Login = author.Login
- jsonBug.Author.Id = author.Id.String()
- jsonBug.Author.HumanId = author.Id.Human()
+ jsonBug.Author = NewJSONIdentityFromExcerpt(author)
} else {
- jsonBug.Author.Name = b.LegacyAuthor.DisplayName()
- jsonBug.Author.Login = b.LegacyAuthor.Login
+ jsonBug.Author = NewJSONIdentityFromLegacyExcerpt(&b.LegacyAuthor)
}
- for _, element := range b.Actors {
+ jsonBug.Actors = make([]JSONIdentity, len(b.Actors))
+ for i, element := range b.Actors {
actor, err := backend.ResolveIdentityExcerpt(element)
if err != nil {
return err
}
-
- jsonBug.Actors = append(jsonBug.Actors, JSONIdentity{
- actor.Id.String(),
- actor.Id.Human(),
- actor.Name,
- actor.Login,
- })
+ jsonBug.Actors[i] = NewJSONIdentityFromExcerpt(actor)
}
- for _, element := range b.Participants {
+ jsonBug.Participants = make([]JSONIdentity, len(b.Participants))
+ for i, element := range b.Participants {
participant, err := backend.ResolveIdentityExcerpt(element)
if err != nil {
return err
}
- jsonBug.Participants = append(jsonBug.Participants, JSONIdentity{
- participant.Id.String(),
- participant.Id.Human(),
- participant.DisplayName(),
- participant.Login,
- })
+ jsonBug.Participants[i] = NewJSONIdentityFromExcerpt(participant)
}
jsonBugs[i] = jsonBug
@@ -207,11 +184,84 @@ func lsDefaultFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerp
func lsPlainFormatter(_ *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error {
for _, b := range bugExcerpts {
- fmt.Printf("[%s] %s\n", b.Status, b.Title)
+ fmt.Printf("%s [%s] %s\n", b.Id.Human(), b.Status, b.Title)
}
return nil
}
+func lsOrgmodeFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error {
+ fmt.Println("+TODO: OPEN | CLOSED")
+
+ for _, b := range bugExcerpts {
+ status := strings.Title(b.Status.String())
+
+ var title string
+ if link, ok := b.CreateMetadata["github-url"]; ok {
+ title = fmt.Sprintf("[%s][%s]", link, b.Title)
+ } else {
+ title = b.Title
+ }
+
+ var name string
+ if b.AuthorId != "" {
+ author, err := backend.ResolveIdentityExcerpt(b.AuthorId)
+ if err != nil {
+ return err
+ }
+ name = author.DisplayName()
+ } else {
+ name = b.LegacyAuthor.DisplayName()
+ }
+
+ labels := b.Labels
+ var labelsString string
+ if len(labels) > 0 {
+ labelsString = fmt.Sprintf(":%s:", strings.Replace(fmt.Sprint(labels), " ", ":", -1))
+ } else {
+ labelsString = ""
+ }
+
+ fmt.Printf("* %s %s [%s] %s: %s %s\n",
+ b.Id.Human(),
+ status,
+ b.CreateTime(),
+ name,
+ title,
+ labelsString,
+ )
+
+ fmt.Printf("** Last Edited: %s\n", b.EditTime().String())
+
+ fmt.Printf("** Actors:\n")
+ for _, element := range b.Actors {
+ actor, err := backend.ResolveIdentityExcerpt(element)
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf(": %s %s\n",
+ actor.Id.Human(),
+ actor.DisplayName(),
+ )
+ }
+
+ fmt.Printf("** Participants:\n")
+ for _, element := range b.Participants {
+ participant, err := backend.ResolveIdentityExcerpt(element)
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf(": %s %s\n",
+ participant.Id.Human(),
+ participant.DisplayName(),
+ )
+ }
+ }
+
+ return nil
+}
+
// Finish the command flags transformation into the query.Query
func completeQuery() error {
for _, str := range lsStatusQuery {
@@ -294,5 +344,5 @@ func init() {
lsCmd.Flags().StringVarP(&lsSortDirection, "direction", "d", "asc",
"Select the sorting direction. Valid values are [asc,desc]")
lsCmd.Flags().StringVarP(&lsOutputFormat, "format", "f", "default",
- "Select the output formatting style. Valid values are [default, plain(text), json]")
+ "Select the output formatting style. Valid values are [default,plain,json,org-mode]")
}
diff --git a/commands/show.go b/commands/show.go
index 0bb3dc4a..2f4e46ed 100644
--- a/commands/show.go
+++ b/commands/show.go
@@ -1,22 +1,26 @@
package commands
import (
+ "encoding/json"
"errors"
"fmt"
"strings"
+ "github.com/spf13/cobra"
+
+ "github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
_select "github.com/MichaelMure/git-bug/commands/select"
"github.com/MichaelMure/git-bug/util/colors"
"github.com/MichaelMure/git-bug/util/interrupt"
- "github.com/spf13/cobra"
)
var (
- showFieldsQuery string
+ showFieldsQuery string
+ showOutputFormat string
)
-func runShowBug(cmd *cobra.Command, args []string) error {
+func runShowBug(_ *cobra.Command, args []string) error {
backend, err := cache.NewRepoCache(repo)
if err != nil {
return err
@@ -35,16 +39,16 @@ func runShowBug(cmd *cobra.Command, args []string) error {
return errors.New("invalid bug: no comment")
}
- firstComment := snapshot.Comments[0]
-
if showFieldsQuery != "" {
switch showFieldsQuery {
case "author":
- fmt.Printf("%s\n", firstComment.Author.DisplayName())
+ fmt.Printf("%s\n", snapshot.Author.DisplayName())
case "authorEmail":
- fmt.Printf("%s\n", firstComment.Author.Email())
+ fmt.Printf("%s\n", snapshot.Author.Email())
case "createTime":
- fmt.Printf("%s\n", firstComment.FormatTime())
+ fmt.Printf("%s\n", snapshot.CreateTime.String())
+ case "lastEdit":
+ fmt.Printf("%s\n", snapshot.EditTime().String())
case "humanId":
fmt.Printf("%s\n", snapshot.Id().Human())
case "id":
@@ -74,16 +78,33 @@ func runShowBug(cmd *cobra.Command, args []string) error {
return nil
}
+ switch showOutputFormat {
+ case "org-mode":
+ return showOrgmodeFormatter(snapshot)
+ case "json":
+ return showJsonFormatter(snapshot)
+ case "default":
+ return showDefaultFormatter(snapshot)
+ default:
+ return fmt.Errorf("unknown format %s", showOutputFormat)
+ }
+}
+
+func showDefaultFormatter(snapshot *bug.Snapshot) error {
// Header
- fmt.Printf("[%s] %s %s\n\n",
- colors.Yellow(snapshot.Status),
+ fmt.Printf("%s [%s] %s\n\n",
colors.Cyan(snapshot.Id().Human()),
+ colors.Yellow(snapshot.Status),
snapshot.Title,
)
- fmt.Printf("%s opened this issue %s\n\n",
- colors.Magenta(firstComment.Author.DisplayName()),
- firstComment.FormatTimeRel(),
+ fmt.Printf("%s opened this issue %s\n",
+ colors.Magenta(snapshot.Author.DisplayName()),
+ snapshot.CreateTime.String(),
+ )
+
+ fmt.Printf("This was last edited at %s\n\n",
+ snapshot.EditTime().String(),
)
// Labels
@@ -143,6 +164,151 @@ func runShowBug(cmd *cobra.Command, args []string) error {
return nil
}
+type JSONBugSnapshot struct {
+ 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"`
+}
+
+type JSONComment struct {
+ 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(),
+ 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))
+ for i, element := range snapshot.Actors {
+ jsonBug.Actors[i] = NewJSONIdentity(element)
+ }
+
+ jsonBug.Participants = make([]JSONIdentity, len(snapshot.Participants))
+ for i, element := range snapshot.Participants {
+ jsonBug.Participants[i] = NewJSONIdentity(element)
+ }
+
+ jsonBug.Comments = make([]JSONComment, len(snapshot.Comments))
+ for i, comment := range snapshot.Comments {
+ jsonBug.Comments[i] = NewJSONComment(comment)
+ }
+
+ jsonObject, _ := json.MarshalIndent(jsonBug, "", " ")
+ fmt.Printf("%s\n", jsonObject)
+
+ return nil
+}
+
+func showOrgmodeFormatter(snapshot *bug.Snapshot) error {
+ // Header
+ fmt.Printf("%s [%s] %s\n",
+ snapshot.Id().Human(),
+ snapshot.Status,
+ snapshot.Title,
+ )
+
+ fmt.Printf("* Author: %s\n",
+ snapshot.Author.DisplayName(),
+ )
+
+ fmt.Printf("* Creation Time: %s\n",
+ snapshot.CreateTime.String(),
+ )
+
+ fmt.Printf("* Last Edit: %s\n",
+ snapshot.EditTime().String(),
+ )
+
+ // Labels
+ var labels = make([]string, len(snapshot.Labels))
+ for i, label := range snapshot.Labels {
+ labels[i] = string(label)
+ }
+
+ fmt.Printf("* Labels:\n")
+ if len(labels) > 0 {
+ fmt.Printf("** %s\n",
+ strings.Join(labels, "\n** "),
+ )
+ }
+
+ // Actors
+ var actors = make([]string, len(snapshot.Actors))
+ for i, actor := range snapshot.Actors {
+ actors[i] = fmt.Sprintf("%s %s",
+ actor.Id().Human(),
+ actor.DisplayName(),
+ )
+ }
+
+ fmt.Printf("* Actors:\n** %s\n",
+ strings.Join(actors, "\n** "),
+ )
+
+ // Participants
+ var participants = make([]string, len(snapshot.Participants))
+ for i, participant := range snapshot.Participants {
+ participants[i] = fmt.Sprintf("%s %s",
+ participant.Id().Human(),
+ participant.DisplayName(),
+ )
+ }
+
+ fmt.Printf("* Participants:\n** %s\n",
+ strings.Join(participants, "\n** "),
+ )
+
+ fmt.Printf("* Comments:\n")
+
+ for i, comment := range snapshot.Comments {
+ var message string
+ fmt.Printf("** #%d %s\n",
+ i,
+ comment.Author.DisplayName(),
+ )
+
+ if comment.Message == "" {
+ message = "No description provided."
+ } else {
+ message = strings.ReplaceAll(comment.Message, "\n", "\n: ")
+ }
+
+ fmt.Printf(": %s\n",
+ message,
+ )
+ }
+
+ return nil
+}
+
var showCmd = &cobra.Command{
Use: "show [<id>]",
Short: "Display the details of a bug.",
@@ -152,6 +318,8 @@ var showCmd = &cobra.Command{
func init() {
RootCmd.AddCommand(showCmd)
- showCmd.Flags().StringVarP(&showFieldsQuery, "field", "f", "",
- "Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]")
+ showCmd.Flags().StringVarP(&showFieldsQuery, "field", "", "",
+ "Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]")
+ showCmd.Flags().StringVarP(&showOutputFormat, "format", "f", "default",
+ "Select the output formatting style. Valid values are [default,json,org-mode]")
}
diff --git a/commands/user_ls.go b/commands/user_ls.go
index 609ff5a4..b3fb32e6 100644
--- a/commands/user_ls.go
+++ b/commands/user_ls.go
@@ -1,15 +1,21 @@
package commands
import (
+ "encoding/json"
"fmt"
+ "github.com/spf13/cobra"
+
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/util/colors"
"github.com/MichaelMure/git-bug/util/interrupt"
- "github.com/spf13/cobra"
)
-func runUserLs(cmd *cobra.Command, args []string) error {
+var (
+ userLsOutputFormat string
+)
+
+func runUserLs(_ *cobra.Command, _ []string) error {
backend, err := cache.NewRepoCache(repo)
if err != nil {
return err
@@ -17,21 +23,48 @@ func runUserLs(cmd *cobra.Command, args []string) error {
defer backend.Close()
interrupt.RegisterCleaner(backend.Close)
- for _, id := range backend.AllIdentityIds() {
- i, err := backend.ResolveIdentityExcerpt(id)
+ ids := backend.AllIdentityIds()
+ var users []*cache.IdentityExcerpt
+ for _, id := range ids {
+ user, err := backend.ResolveIdentityExcerpt(id)
if err != nil {
return err
}
+ users = append(users, user)
+ }
+
+ switch userLsOutputFormat {
+ case "json":
+ return userLsJsonFormatter(users)
+ case "default":
+ return userLsDefaultFormatter(users)
+ default:
+ return fmt.Errorf("unknown format %s", userLsOutputFormat)
+ }
+}
+func userLsDefaultFormatter(users []*cache.IdentityExcerpt) error {
+ for _, user := range users {
fmt.Printf("%s %s\n",
- colors.Cyan(i.Id.Human()),
- i.DisplayName(),
+ colors.Cyan(user.Id.Human()),
+ user.DisplayName(),
)
}
return nil
}
+func userLsJsonFormatter(users []*cache.IdentityExcerpt) error {
+ jsonUsers := make([]JSONIdentity, len(users))
+ for i, user := range users {
+ jsonUsers[i] = NewJSONIdentityFromExcerpt(user)
+ }
+
+ jsonObject, _ := json.MarshalIndent(jsonUsers, "", " ")
+ fmt.Printf("%s\n", jsonObject)
+ return nil
+}
+
var userLsCmd = &cobra.Command{
Use: "ls",
Short: "List identities.",
@@ -42,4 +75,6 @@ var userLsCmd = &cobra.Command{
func init() {
userCmd.AddCommand(userLsCmd)
userLsCmd.Flags().SortFlags = false
+ userLsCmd.Flags().StringVarP(&userLsOutputFormat, "format", "f", "default",
+ "Select the output formatting style. Valid values are [default,json]")
}
diff --git a/doc/man/git-bug-ls.1 b/doc/man/git-bug-ls.1
index 5d05cfcd..54a64cd2 100644
--- a/doc/man/git-bug-ls.1
+++ b/doc/man/git-bug-ls.1
@@ -59,7 +59,7 @@ You can pass an additional query to filter and order the list. This query can be
.PP
\fB\-f\fP, \fB\-\-format\fP="default"
- Select the output formatting style. Valid values are [default, plain(text), json]
+ Select the output formatting style. Valid values are [default,plain,json,org\-mode]
.PP
\fB\-h\fP, \fB\-\-help\fP[=false]
diff --git a/doc/man/git-bug-show.1 b/doc/man/git-bug-show.1
index dae1877b..eb694dac 100644
--- a/doc/man/git-bug-show.1
+++ b/doc/man/git-bug-show.1
@@ -19,8 +19,12 @@ Display the details of a bug.
.SH OPTIONS
.PP
-\fB\-f\fP, \fB\-\-field\fP=""
- Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]
+\fB\-\-field\fP=""
+ Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]
+
+.PP
+\fB\-f\fP, \fB\-\-format\fP="default"
+ Select the output formatting style. Valid values are [default,json,org\-mode]
.PP
\fB\-h\fP, \fB\-\-help\fP[=false]
diff --git a/doc/man/git-bug-user-ls.1 b/doc/man/git-bug-user-ls.1
index 1ccddb8a..14bc9ff7 100644
--- a/doc/man/git-bug-user-ls.1
+++ b/doc/man/git-bug-user-ls.1
@@ -19,6 +19,10 @@ List identities.
.SH OPTIONS
.PP
+\fB\-f\fP, \fB\-\-format\fP="default"
+ Select the output formatting style. Valid values are [default,json]
+
+.PP
\fB\-h\fP, \fB\-\-help\fP[=false]
help for ls
diff --git a/doc/md/git-bug_ls.md b/doc/md/git-bug_ls.md
index 32941e4f..1532efab 100644
--- a/doc/md/git-bug_ls.md
+++ b/doc/md/git-bug_ls.md
@@ -35,7 +35,7 @@ git bug ls --status closed --by creation
-n, --no strings Filter by absence of something. Valid values are [label]
-b, --by string Sort the results by a characteristic. Valid values are [id,creation,edit] (default "creation")
-d, --direction string Select the sorting direction. Valid values are [asc,desc] (default "asc")
- -f, --format string Select the output formatting style. Valid values are [default, plain(text), json] (default "default")
+ -f, --format string Select the output formatting style. Valid values are [default,plain,json,org-mode] (default "default")
-h, --help help for ls
```
diff --git a/doc/md/git-bug_show.md b/doc/md/git-bug_show.md
index 40152ede..e6d2d7ba 100644
--- a/doc/md/git-bug_show.md
+++ b/doc/md/git-bug_show.md
@@ -13,8 +13,9 @@ git-bug show [<id>] [flags]
### Options
```
- -f, --field string Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]
- -h, --help help for show
+ --field string Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]
+ -f, --format string Select the output formatting style. Valid values are [default,json,org-mode] (default "default")
+ -h, --help help for show
```
### SEE ALSO
diff --git a/doc/md/git-bug_user_ls.md b/doc/md/git-bug_user_ls.md
index d390ed2a..51f01712 100644
--- a/doc/md/git-bug_user_ls.md
+++ b/doc/md/git-bug_user_ls.md
@@ -13,7 +13,8 @@ git-bug user ls [flags]
### Options
```
- -h, --help help for ls
+ -f, --format string Select the output formatting style. Valid values are [default,json] (default "default")
+ -h, --help help for ls
```
### SEE ALSO
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/misc/bash_completion/git-bug b/misc/bash_completion/git-bug
index 93c75b8e..c3e62849 100644
--- a/misc/bash_completion/git-bug
+++ b/misc/bash_completion/git-bug
@@ -933,8 +933,11 @@ _git-bug_show()
flags+=("--field=")
two_word_flags+=("--field")
- two_word_flags+=("-f")
local_nonpersistent_flags+=("--field=")
+ flags+=("--format=")
+ two_word_flags+=("--format")
+ two_word_flags+=("-f")
+ local_nonpersistent_flags+=("--format=")
must_have_one_flag=()
must_have_one_noun=()
@@ -1122,6 +1125,10 @@ _git-bug_user_ls()
flags_with_completion=()
flags_completion=()
+ flags+=("--format=")
+ two_word_flags+=("--format")
+ two_word_flags+=("-f")
+ local_nonpersistent_flags+=("--format=")
must_have_one_flag=()
must_have_one_noun=()
diff --git a/misc/powershell_completion/git-bug b/misc/powershell_completion/git-bug
index 9f51c785..0a8c7b5a 100644
--- a/misc/powershell_completion/git-bug
+++ b/misc/powershell_completion/git-bug
@@ -159,8 +159,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
[CompletionResult]::new('--by', 'by', [CompletionResultType]::ParameterName, 'Sort the results by a characteristic. Valid values are [id,creation,edit]')
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]')
[CompletionResult]::new('--direction', 'direction', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]')
- [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default, plain(text), json]')
- [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default, plain(text), json]')
+ [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,plain,json,org-mode]')
+ [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,plain,json,org-mode]')
break
}
'git-bug;ls-id' {
@@ -179,8 +179,9 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
break
}
'git-bug;show' {
- [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]')
- [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]')
+ [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]')
+ [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json,org-mode]')
+ [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json,org-mode]')
break
}
'git-bug;status' {
@@ -221,6 +222,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
break
}
'git-bug;user;ls' {
+ [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json]')
+ [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json]')
break
}
'git-bug;version' {
@@ -242,4 +245,4 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
})
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
Sort-Object -Property ListItemText
-}
+} \ No newline at end of file
diff --git a/misc/zsh_completion/git-bug b/misc/zsh_completion/git-bug
index 18a49668..ba15f3bc 100644
--- a/misc/zsh_completion/git-bug
+++ b/misc/zsh_completion/git-bug
@@ -304,7 +304,7 @@ function _git-bug_ls {
'(*-n *--no)'{\*-n,\*--no}'[Filter by absence of something. Valid values are [label]]:' \
'(-b --by)'{-b,--by}'[Sort the results by a characteristic. Valid values are [id,creation,edit]]:' \
'(-d --direction)'{-d,--direction}'[Select the sorting direction. Valid values are [asc,desc]]:' \
- '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default, plain(text), json]]:'
+ '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default,plain,json,org-mode]]:'
}
function _git-bug_ls-id {
@@ -329,7 +329,8 @@ function _git-bug_select {
function _git-bug_show {
_arguments \
- '(-f --field)'{-f,--field}'[Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]]:'
+ '--field[Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]]:' \
+ '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default,json,org-mode]]:'
}
@@ -443,7 +444,8 @@ function _git-bug_user_create {
}
function _git-bug_user_ls {
- _arguments
+ _arguments \
+ '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default,json]]:'
}
function _git-bug_version {
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)