From 7a80d8f849861a6033cd0765e5d85a52b08a8854 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 24 Feb 2019 14:17:52 +0100 Subject: commands: add a super-fast "user ls" command --- cache/bug_excerpt.go | 4 ++++ cache/identity_excerpt.go | 26 +++++++++++++++++++++--- cache/repo_cache.go | 12 +++++++++++ commands/user_list.go | 47 ++++++++++++++++++++++++++++++++++++++++++++ doc/man/git-bug-user-ls.1 | 33 +++++++++++++++++++++++++++++++ doc/man/git-bug-user.1 | 2 +- doc/md/git-bug_user.md | 1 + doc/md/git-bug_user_ls.md | 23 ++++++++++++++++++++++ identity/bare.go | 5 +++++ identity/identity.go | 17 ++++++++++++++++ identity/identity_stub.go | 4 ++++ identity/interface.go | 3 +++ misc/bash_completion/git-bug | 24 ++++++++++++++++++++++ misc/zsh_completion/git-bug | 2 +- 14 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 commands/user_list.go create mode 100644 doc/man/git-bug-user-ls.1 create mode 100644 doc/md/git-bug_user_ls.md diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index 55518077..fd06e51b 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -68,6 +68,10 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { return e } +func (b *BugExcerpt) HumanId() string { + return bug.FormatHumanID(b.Id) +} + /* * Sorting */ diff --git a/cache/identity_excerpt.go b/cache/identity_excerpt.go index 0539a76b..2a13bc60 100644 --- a/cache/identity_excerpt.go +++ b/cache/identity_excerpt.go @@ -2,10 +2,16 @@ package cache import ( "encoding/gob" + "fmt" "github.com/MichaelMure/git-bug/identity" ) +// Package initialisation used to register the type for (de)serialization +func init() { + gob.Register(IdentityExcerpt{}) +} + // IdentityExcerpt hold a subset of the identity values to be able to sort and // filter identities efficiently without having to read and compile each raw // identity. @@ -26,9 +32,23 @@ func NewIdentityExcerpt(i *identity.Identity) *IdentityExcerpt { } } -// Package initialisation used to register the type for (de)serialization -func init() { - gob.Register(IdentityExcerpt{}) +func (i *IdentityExcerpt) HumanId() string { + return identity.FormatHumanID(i.Id) +} + +// DisplayName return a non-empty string to display, representing the +// identity, based on the non-empty values. +func (i *IdentityExcerpt) DisplayName() string { + switch { + case i.Name == "" && i.Login != "": + return i.Login + case i.Name != "" && i.Login == "": + return i.Name + case i.Name != "" && i.Login != "": + return fmt.Sprintf("%s (%s)", i.Name, i.Login) + } + + panic("invalid person data") } /* diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 78633a69..a50c745b 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -489,6 +489,12 @@ func (c *RepoCache) AllBugsIds() []string { return result } +// AllBugExcerpt return all known bug excerpt. +// This maps is read-only. +func (c *RepoCache) AllBugExcerpt() map[string]*BugExcerpt { + return c.bugExcerpts +} + // ValidLabels list valid labels // // Note: in the future, a proper label policy could be implemented where valid @@ -765,6 +771,12 @@ func (c *RepoCache) AllIdentityIds() []string { return result } +// AllIdentityExcerpt return all known identities excerpt. +// This maps is read-only. +func (c *RepoCache) AllIdentityExcerpt() map[string]*IdentityExcerpt { + return c.identitiesExcerpts +} + func (c *RepoCache) SetUserIdentity(i *IdentityCache) error { err := identity.SetUserIdentity(c.repo, i.Identity) if err != nil { diff --git a/commands/user_list.go b/commands/user_list.go new file mode 100644 index 00000000..4d6e5e12 --- /dev/null +++ b/commands/user_list.go @@ -0,0 +1,47 @@ +package commands + +import ( + "fmt" + + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/util/colors" + "github.com/MichaelMure/git-bug/util/interrupt" + "github.com/spf13/cobra" +) + +var ( + userLsVerbose bool +) + +func runUserLs(cmd *cobra.Command, args []string) error { + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + interrupt.RegisterCleaner(backend.Close) + + for _, i := range backend.AllIdentityExcerpt() { + fmt.Printf("%s %s\n", + colors.Cyan(i.HumanId()), + i.DisplayName(), + ) + } + + return nil +} + +var userLsCmd = &cobra.Command{ + Use: "ls", + Short: "List identities", + PreRunE: loadRepo, + RunE: runUserLs, +} + +func init() { + userCmd.AddCommand(userLsCmd) + userLsCmd.Flags().SortFlags = false + + userLsCmd.Flags().BoolVarP(&userLsVerbose, "verbose", "v", false, + "Print extra information") +} diff --git a/doc/man/git-bug-user-ls.1 b/doc/man/git-bug-user-ls.1 new file mode 100644 index 00000000..6820a28d --- /dev/null +++ b/doc/man/git-bug-user-ls.1 @@ -0,0 +1,33 @@ +.TH "GIT-BUG" "1" "Feb 2019" "Generated from git-bug's source code" "" +.nh +.ad l + + +.SH NAME +.PP +git\-bug\-user\-ls \- List identities + + +.SH SYNOPSIS +.PP +\fBgit\-bug user ls [flags]\fP + + +.SH DESCRIPTION +.PP +List identities + + +.SH OPTIONS +.PP +\fB\-v\fP, \fB\-\-verbose\fP[=false] + Print extra information + +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for ls + + +.SH SEE ALSO +.PP +\fBgit\-bug\-user(1)\fP diff --git a/doc/man/git-bug-user.1 b/doc/man/git-bug-user.1 index eb074973..acb9259f 100644 --- a/doc/man/git-bug-user.1 +++ b/doc/man/git-bug-user.1 @@ -26,4 +26,4 @@ Display or change the user identity .SH SEE ALSO .PP -\fBgit\-bug(1)\fP, \fBgit\-bug\-user\-create(1)\fP +\fBgit\-bug(1)\fP, \fBgit\-bug\-user\-create(1)\fP, \fBgit\-bug\-user\-ls(1)\fP diff --git a/doc/md/git-bug_user.md b/doc/md/git-bug_user.md index 5692b40a..f6a3d91b 100644 --- a/doc/md/git-bug_user.md +++ b/doc/md/git-bug_user.md @@ -20,4 +20,5 @@ git-bug user [] [flags] * [git-bug](git-bug.md) - A bug tracker embedded in Git * [git-bug user create](git-bug_user_create.md) - Create a new identity +* [git-bug user ls](git-bug_user_ls.md) - List identities diff --git a/doc/md/git-bug_user_ls.md b/doc/md/git-bug_user_ls.md new file mode 100644 index 00000000..9c9651f5 --- /dev/null +++ b/doc/md/git-bug_user_ls.md @@ -0,0 +1,23 @@ +## git-bug user ls + +List identities + +### Synopsis + +List identities + +``` +git-bug user ls [flags] +``` + +### Options + +``` + -v, --verbose Print extra information + -h, --help help for ls +``` + +### SEE ALSO + +* [git-bug user](git-bug_user.md) - Display or change the user identity + diff --git a/identity/bare.go b/identity/bare.go index d3f7655a..b6cbe491 100644 --- a/identity/bare.go +++ b/identity/bare.go @@ -85,6 +85,11 @@ func (i *Bare) Id() string { return i.id } +// HumanId return the Identity identifier truncated for human consumption +func (i *Bare) HumanId() string { + return FormatHumanID(i.Id()) +} + // Name return the last version of the name func (i *Bare) Name() string { return i.name diff --git a/identity/identity.go b/identity/identity.go index 114b954e..d57e8ce0 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -20,6 +20,9 @@ const identityRemoteRefPattern = "refs/remotes/%s/identities/" const versionEntryName = "version" const identityConfigKey = "git-bug.identity" +const idLength = 40 +const humanIdLength = 7 + var ErrNonFastForwardMerge = errors.New("non fast-forward identity merge") var ErrNoIdentitySet = errors.New("user identity first needs to be created using \"git bug user create\"") var ErrMultipleIdentitiesSet = errors.New("multiple user identities set") @@ -93,6 +96,10 @@ func read(repo repository.Repo, ref string) (*Identity, error) { refSplit := strings.Split(ref, "/") id := refSplit[len(refSplit)-1] + if len(id) != idLength { + return nil, fmt.Errorf("invalid ref length") + } + hashes, err := repo.ListCommits(ref) // TODO: this is not perfect, it might be a command invoke error @@ -461,6 +468,16 @@ func (i *Identity) Id() string { return i.id } +// HumanId return the Identity identifier truncated for human consumption +func (i *Identity) HumanId() string { + return FormatHumanID(i.Id()) +} + +func FormatHumanID(id string) string { + format := fmt.Sprintf("%%.%ds", humanIdLength) + return fmt.Sprintf(format, id) +} + // Name return the last version of the name func (i *Identity) Name() string { return i.lastVersion().name diff --git a/identity/identity_stub.go b/identity/identity_stub.go index 830cfb99..1bfc18d0 100644 --- a/identity/identity_stub.go +++ b/identity/identity_stub.go @@ -44,6 +44,10 @@ func (i *IdentityStub) Id() string { return i.id } +func (i *IdentityStub) HumanId() string { + return FormatHumanID(i.Id()) +} + func (IdentityStub) Name() string { panic("identities needs to be properly loaded with identity.ReadLocal()") } diff --git a/identity/interface.go b/identity/interface.go index 9fe4db4f..d5c80543 100644 --- a/identity/interface.go +++ b/identity/interface.go @@ -9,6 +9,9 @@ type Interface interface { // Id return the Identity identifier Id() string + // HumanId return the Identity identifier truncated for human consumption + HumanId() string + // Name return the last version of the name Name() string // Email return the last version of the email diff --git a/misc/bash_completion/git-bug b/misc/bash_completion/git-bug index 4ec1e472..237a0df0 100644 --- a/misc/bash_completion/git-bug +++ b/misc/bash_completion/git-bug @@ -819,6 +819,29 @@ _git-bug_user_create() noun_aliases=() } +_git-bug_user_ls() +{ + last_command="git-bug_user_ls" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--verbose") + flags+=("-v") + local_nonpersistent_flags+=("--verbose") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _git-bug_user() { last_command="git-bug_user" @@ -827,6 +850,7 @@ _git-bug_user() commands=() commands+=("create") + commands+=("ls") flags=() two_word_flags=() diff --git a/misc/zsh_completion/git-bug b/misc/zsh_completion/git-bug index 1a705f7d..6fc3cf8e 100644 --- a/misc/zsh_completion/git-bug +++ b/misc/zsh_completion/git-bug @@ -33,7 +33,7 @@ case $state in _arguments '2: :(edit)' ;; user) - _arguments '2: :(create)' + _arguments '2: :(create ls)' ;; *) _arguments '*: :_files' -- cgit