deal with the previous changes

Michael Muré created

Change summary

api/graphql/models/lazy_identity.go    | 49 +-----------------
bridge/github/import.go                |  2 
bridge/github/import_test.go           | 64 ++++++++++++------------
bridge/gitlab/import.go                |  1 
bridge/gitlab/import_test.go           | 72 +++++++++++++-------------
bridge/jira/import.go                  |  1 
bridge/launchpad/import.go             |  1 
cache/identity_cache.go                |  8 ++
cache/repo_cache.go                    |  3 
cache/repo_cache_identity.go           | 13 +++-
commands/user.go                       | 14 +++-
commands/user_create.go                |  2 
doc/man/git-bug-user.1                 |  2 
doc/md/git-bug_user.md                 |  2 
entity/id.go                           |  6 +-
entity/interface.go                    |  6 ++
go.sum                                 |  1 
misc/powershell_completion/git-bug     |  4 
misc/random_bugs/create_random_bugs.go | 11 ++-
19 files changed, 124 insertions(+), 138 deletions(-)

Detailed changes

api/graphql/models/lazy_identity.go 🔗

@@ -7,8 +7,6 @@ import (
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/entity"
 	"github.com/MichaelMure/git-bug/identity"
-	"github.com/MichaelMure/git-bug/util/lamport"
-	"github.com/MichaelMure/git-bug/util/timestamp"
 )
 
 // IdentityWrapper is an interface used by the GraphQL resolvers to handle an identity.
@@ -21,11 +19,8 @@ type IdentityWrapper interface {
 	Login() (string, error)
 	AvatarUrl() (string, error)
 	Keys() ([]*identity.Key, error)
-	ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error)
 	DisplayName() string
 	IsProtected() (bool, error)
-	LastModificationLamport() (lamport.Time, error)
-	LastModification() (timestamp.Timestamp, error)
 }
 
 var _ IdentityWrapper = &lazyIdentity{}
@@ -69,6 +64,10 @@ func (li *lazyIdentity) Name() string {
 	return li.excerpt.Name
 }
 
+func (li *lazyIdentity) DisplayName() string {
+	return li.excerpt.DisplayName()
+}
+
 func (li *lazyIdentity) Email() (string, error) {
 	id, err := li.load()
 	if err != nil {
@@ -101,18 +100,6 @@ func (li *lazyIdentity) Keys() ([]*identity.Key, error) {
 	return id.Keys(), nil
 }
 
-func (li *lazyIdentity) ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error) {
-	id, err := li.load()
-	if err != nil {
-		return nil, err
-	}
-	return id.ValidKeysAtTime(time), nil
-}
-
-func (li *lazyIdentity) DisplayName() string {
-	return li.excerpt.DisplayName()
-}
-
 func (li *lazyIdentity) IsProtected() (bool, error) {
 	id, err := li.load()
 	if err != nil {
@@ -121,22 +108,6 @@ func (li *lazyIdentity) IsProtected() (bool, error) {
 	return id.IsProtected(), nil
 }
 
-func (li *lazyIdentity) LastModificationLamport() (lamport.Time, error) {
-	id, err := li.load()
-	if err != nil {
-		return 0, err
-	}
-	return id.LastModificationLamport(), nil
-}
-
-func (li *lazyIdentity) LastModification() (timestamp.Timestamp, error) {
-	id, err := li.load()
-	if err != nil {
-		return 0, err
-	}
-	return id.LastModification(), nil
-}
-
 var _ IdentityWrapper = &loadedIdentity{}
 
 type loadedIdentity struct {
@@ -163,18 +134,6 @@ func (l loadedIdentity) Keys() ([]*identity.Key, error) {
 	return l.Interface.Keys(), nil
 }
 
-func (l loadedIdentity) ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error) {
-	return l.Interface.ValidKeysAtTime(time), nil
-}
-
 func (l loadedIdentity) IsProtected() (bool, error) {
 	return l.Interface.IsProtected(), nil
 }
-
-func (l loadedIdentity) LastModificationLamport() (lamport.Time, error) {
-	return l.Interface.LastModificationLamport(), nil
-}
-
-func (l loadedIdentity) LastModification() (timestamp.Timestamp, error) {
-	return l.Interface.LastModification(), nil
-}

bridge/github/import.go 🔗

@@ -551,6 +551,7 @@ func (gi *githubImporter) ensurePerson(repo *cache.RepoCache, actor *actor) (*ca
 		email,
 		string(actor.Login),
 		string(actor.AvatarUrl),
+		nil,
 		map[string]string{
 			metaKeyGithubLogin: string(actor.Login),
 		},
@@ -598,6 +599,7 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache,
 		"",
 		string(q.User.Login),
 		string(q.User.AvatarUrl),
+		nil,
 		map[string]string{
 			metaKeyGithubLogin: string(q.User.Login),
 		},

bridge/github/import_test.go 🔗

@@ -7,7 +7,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
 	"github.com/MichaelMure/git-bug/bridge/core"
@@ -20,7 +19,22 @@ import (
 )
 
 func Test_Importer(t *testing.T) {
-	author := identity.NewIdentity("Michael Muré", "batolettre@gmail.com")
+	envToken := os.Getenv("GITHUB_TOKEN_PRIVATE")
+	if envToken == "" {
+		t.Skip("Env var GITHUB_TOKEN_PRIVATE missing")
+	}
+
+	repo := repository.CreateGoGitTestRepo(false)
+	defer repository.CleanupTestRepos(repo)
+
+	backend, err := cache.NewRepoCache(repo)
+	require.NoError(t, err)
+
+	defer backend.Close()
+	interrupt.RegisterCleaner(backend.Close)
+
+	author, err := identity.NewIdentity(repo, "Michael Muré", "batolettre@gmail.com")
+	require.NoError(t, err)
 
 	tests := []struct {
 		name string
@@ -127,20 +141,6 @@ func Test_Importer(t *testing.T) {
 		},
 	}
 
-	repo := repository.CreateGoGitTestRepo(false)
-	defer repository.CleanupTestRepos(repo)
-
-	backend, err := cache.NewRepoCache(repo)
-	require.NoError(t, err)
-
-	defer backend.Close()
-	interrupt.RegisterCleaner(backend.Close)
-
-	envToken := os.Getenv("GITHUB_TOKEN_PRIVATE")
-	if envToken == "" {
-		t.Skip("Env var GITHUB_TOKEN_PRIVATE missing")
-	}
-
 	login := "test-identity"
 	author.SetMetadata(metaKeyGithubLogin, login)
 
@@ -178,33 +178,33 @@ func Test_Importer(t *testing.T) {
 			require.NoError(t, err)
 
 			ops := b.Snapshot().Operations
-			assert.Len(t, tt.bug.Operations, len(b.Snapshot().Operations))
+			require.Len(t, tt.bug.Operations, len(b.Snapshot().Operations))
 
 			for i, op := range tt.bug.Operations {
 				require.IsType(t, ops[i], op)
 
 				switch op.(type) {
 				case *bug.CreateOperation:
-					assert.Equal(t, op.(*bug.CreateOperation).Title, ops[i].(*bug.CreateOperation).Title)
-					assert.Equal(t, op.(*bug.CreateOperation).Message, ops[i].(*bug.CreateOperation).Message)
-					assert.Equal(t, op.(*bug.CreateOperation).Author.Name(), ops[i].(*bug.CreateOperation).Author.Name())
+					require.Equal(t, op.(*bug.CreateOperation).Title, ops[i].(*bug.CreateOperation).Title)
+					require.Equal(t, op.(*bug.CreateOperation).Message, ops[i].(*bug.CreateOperation).Message)
+					require.Equal(t, op.(*bug.CreateOperation).Author.Name(), ops[i].(*bug.CreateOperation).Author.Name())
 				case *bug.SetStatusOperation:
-					assert.Equal(t, op.(*bug.SetStatusOperation).Status, ops[i].(*bug.SetStatusOperation).Status)
-					assert.Equal(t, op.(*bug.SetStatusOperation).Author.Name(), ops[i].(*bug.SetStatusOperation).Author.Name())
+					require.Equal(t, op.(*bug.SetStatusOperation).Status, ops[i].(*bug.SetStatusOperation).Status)
+					require.Equal(t, op.(*bug.SetStatusOperation).Author.Name(), ops[i].(*bug.SetStatusOperation).Author.Name())
 				case *bug.SetTitleOperation:
-					assert.Equal(t, op.(*bug.SetTitleOperation).Was, ops[i].(*bug.SetTitleOperation).Was)
-					assert.Equal(t, op.(*bug.SetTitleOperation).Title, ops[i].(*bug.SetTitleOperation).Title)
-					assert.Equal(t, op.(*bug.SetTitleOperation).Author.Name(), ops[i].(*bug.SetTitleOperation).Author.Name())
+					require.Equal(t, op.(*bug.SetTitleOperation).Was, ops[i].(*bug.SetTitleOperation).Was)
+					require.Equal(t, op.(*bug.SetTitleOperation).Title, ops[i].(*bug.SetTitleOperation).Title)
+					require.Equal(t, op.(*bug.SetTitleOperation).Author.Name(), ops[i].(*bug.SetTitleOperation).Author.Name())
 				case *bug.LabelChangeOperation:
-					assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Added, ops[i].(*bug.LabelChangeOperation).Added)
-					assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Removed, ops[i].(*bug.LabelChangeOperation).Removed)
-					assert.Equal(t, op.(*bug.LabelChangeOperation).Author.Name(), ops[i].(*bug.LabelChangeOperation).Author.Name())
+					require.ElementsMatch(t, op.(*bug.LabelChangeOperation).Added, ops[i].(*bug.LabelChangeOperation).Added)
+					require.ElementsMatch(t, op.(*bug.LabelChangeOperation).Removed, ops[i].(*bug.LabelChangeOperation).Removed)
+					require.Equal(t, op.(*bug.LabelChangeOperation).Author.Name(), ops[i].(*bug.LabelChangeOperation).Author.Name())
 				case *bug.AddCommentOperation:
-					assert.Equal(t, op.(*bug.AddCommentOperation).Message, ops[i].(*bug.AddCommentOperation).Message)
-					assert.Equal(t, op.(*bug.AddCommentOperation).Author.Name(), ops[i].(*bug.AddCommentOperation).Author.Name())
+					require.Equal(t, op.(*bug.AddCommentOperation).Message, ops[i].(*bug.AddCommentOperation).Message)
+					require.Equal(t, op.(*bug.AddCommentOperation).Author.Name(), ops[i].(*bug.AddCommentOperation).Author.Name())
 				case *bug.EditCommentOperation:
-					assert.Equal(t, op.(*bug.EditCommentOperation).Message, ops[i].(*bug.EditCommentOperation).Message)
-					assert.Equal(t, op.(*bug.EditCommentOperation).Author.Name(), ops[i].(*bug.EditCommentOperation).Author.Name())
+					require.Equal(t, op.(*bug.EditCommentOperation).Message, ops[i].(*bug.EditCommentOperation).Message)
+					require.Equal(t, op.(*bug.EditCommentOperation).Author.Name(), ops[i].(*bug.EditCommentOperation).Author.Name())
 
 				default:
 					panic("unknown operation type")

bridge/gitlab/import.go 🔗

@@ -406,6 +406,7 @@ func (gi *gitlabImporter) ensurePerson(repo *cache.RepoCache, id int) (*cache.Id
 		user.PublicEmail,
 		user.Username,
 		user.AvatarURL,
+		nil,
 		map[string]string{
 			// because Gitlab
 			metaKeyGitlabId:    strconv.Itoa(id),

bridge/gitlab/import_test.go 🔗

@@ -7,7 +7,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
 	"github.com/MichaelMure/git-bug/bridge/core"
@@ -20,7 +19,27 @@ import (
 )
 
 func TestImport(t *testing.T) {
-	author := identity.NewIdentity("Amine Hilaly", "hilalyamine@gmail.com")
+	envToken := os.Getenv("GITLAB_API_TOKEN")
+	if envToken == "" {
+		t.Skip("Env var GITLAB_API_TOKEN missing")
+	}
+
+	projectID := os.Getenv("GITLAB_PROJECT_ID")
+	if projectID == "" {
+		t.Skip("Env var GITLAB_PROJECT_ID missing")
+	}
+
+	repo := repository.CreateGoGitTestRepo(false)
+	defer repository.CleanupTestRepos(repo)
+
+	backend, err := cache.NewRepoCache(repo)
+	require.NoError(t, err)
+
+	defer backend.Close()
+	interrupt.RegisterCleaner(backend.Close)
+
+	author, err := identity.NewIdentity(repo, "Amine Hilaly", "hilalyamine@gmail.com")
+	require.NoError(t, err)
 
 	tests := []struct {
 		name string
@@ -76,25 +95,6 @@ func TestImport(t *testing.T) {
 		},
 	}
 
-	repo := repository.CreateGoGitTestRepo(false)
-	defer repository.CleanupTestRepos(repo)
-
-	backend, err := cache.NewRepoCache(repo)
-	require.NoError(t, err)
-
-	defer backend.Close()
-	interrupt.RegisterCleaner(backend.Close)
-
-	envToken := os.Getenv("GITLAB_API_TOKEN")
-	if envToken == "" {
-		t.Skip("Env var GITLAB_API_TOKEN missing")
-	}
-
-	projectID := os.Getenv("GITLAB_PROJECT_ID")
-	if projectID == "" {
-		t.Skip("Env var GITLAB_PROJECT_ID missing")
-	}
-
 	login := "test-identity"
 	author.SetMetadata(metaKeyGitlabLogin, login)
 
@@ -141,26 +141,26 @@ func TestImport(t *testing.T) {
 
 				switch op.(type) {
 				case *bug.CreateOperation:
-					assert.Equal(t, op.(*bug.CreateOperation).Title, ops[i].(*bug.CreateOperation).Title)
-					assert.Equal(t, op.(*bug.CreateOperation).Message, ops[i].(*bug.CreateOperation).Message)
-					assert.Equal(t, op.(*bug.CreateOperation).Author.Name(), ops[i].(*bug.CreateOperation).Author.Name())
+					require.Equal(t, op.(*bug.CreateOperation).Title, ops[i].(*bug.CreateOperation).Title)
+					require.Equal(t, op.(*bug.CreateOperation).Message, ops[i].(*bug.CreateOperation).Message)
+					require.Equal(t, op.(*bug.CreateOperation).Author.Name(), ops[i].(*bug.CreateOperation).Author.Name())
 				case *bug.SetStatusOperation:
-					assert.Equal(t, op.(*bug.SetStatusOperation).Status, ops[i].(*bug.SetStatusOperation).Status)
-					assert.Equal(t, op.(*bug.SetStatusOperation).Author.Name(), ops[i].(*bug.SetStatusOperation).Author.Name())
+					require.Equal(t, op.(*bug.SetStatusOperation).Status, ops[i].(*bug.SetStatusOperation).Status)
+					require.Equal(t, op.(*bug.SetStatusOperation).Author.Name(), ops[i].(*bug.SetStatusOperation).Author.Name())
 				case *bug.SetTitleOperation:
-					assert.Equal(t, op.(*bug.SetTitleOperation).Was, ops[i].(*bug.SetTitleOperation).Was)
-					assert.Equal(t, op.(*bug.SetTitleOperation).Title, ops[i].(*bug.SetTitleOperation).Title)
-					assert.Equal(t, op.(*bug.SetTitleOperation).Author.Name(), ops[i].(*bug.SetTitleOperation).Author.Name())
+					require.Equal(t, op.(*bug.SetTitleOperation).Was, ops[i].(*bug.SetTitleOperation).Was)
+					require.Equal(t, op.(*bug.SetTitleOperation).Title, ops[i].(*bug.SetTitleOperation).Title)
+					require.Equal(t, op.(*bug.SetTitleOperation).Author.Name(), ops[i].(*bug.SetTitleOperation).Author.Name())
 				case *bug.LabelChangeOperation:
-					assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Added, ops[i].(*bug.LabelChangeOperation).Added)
-					assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Removed, ops[i].(*bug.LabelChangeOperation).Removed)
-					assert.Equal(t, op.(*bug.LabelChangeOperation).Author.Name(), ops[i].(*bug.LabelChangeOperation).Author.Name())
+					require.ElementsMatch(t, op.(*bug.LabelChangeOperation).Added, ops[i].(*bug.LabelChangeOperation).Added)
+					require.ElementsMatch(t, op.(*bug.LabelChangeOperation).Removed, ops[i].(*bug.LabelChangeOperation).Removed)
+					require.Equal(t, op.(*bug.LabelChangeOperation).Author.Name(), ops[i].(*bug.LabelChangeOperation).Author.Name())
 				case *bug.AddCommentOperation:
-					assert.Equal(t, op.(*bug.AddCommentOperation).Message, ops[i].(*bug.AddCommentOperation).Message)
-					assert.Equal(t, op.(*bug.AddCommentOperation).Author.Name(), ops[i].(*bug.AddCommentOperation).Author.Name())
+					require.Equal(t, op.(*bug.AddCommentOperation).Message, ops[i].(*bug.AddCommentOperation).Message)
+					require.Equal(t, op.(*bug.AddCommentOperation).Author.Name(), ops[i].(*bug.AddCommentOperation).Author.Name())
 				case *bug.EditCommentOperation:
-					assert.Equal(t, op.(*bug.EditCommentOperation).Message, ops[i].(*bug.EditCommentOperation).Message)
-					assert.Equal(t, op.(*bug.EditCommentOperation).Author.Name(), ops[i].(*bug.EditCommentOperation).Author.Name())
+					require.Equal(t, op.(*bug.EditCommentOperation).Message, ops[i].(*bug.EditCommentOperation).Message)
+					require.Equal(t, op.(*bug.EditCommentOperation).Author.Name(), ops[i].(*bug.EditCommentOperation).Author.Name())
 
 				default:
 					panic("unknown operation type")

bridge/jira/import.go 🔗

@@ -196,6 +196,7 @@ func (ji *jiraImporter) ensurePerson(repo *cache.RepoCache, user User) (*cache.I
 		user.EmailAddress,
 		user.Key,
 		"",
+		nil,
 		map[string]string{
 			metaKeyJiraUser: user.Key,
 		},

bridge/launchpad/import.go 🔗

@@ -35,6 +35,7 @@ func (li *launchpadImporter) ensurePerson(repo *cache.RepoCache, owner LPPerson)
 		"",
 		owner.Login,
 		"",
+		nil,
 		map[string]string{
 			metaKeyLaunchpadLogin: owner.Login,
 		},

cache/identity_cache.go 🔗

@@ -2,6 +2,7 @@ package cache
 
 import (
 	"github.com/MichaelMure/git-bug/identity"
+	"github.com/MichaelMure/git-bug/repository"
 )
 
 var _ identity.Interface = &IdentityCache{}
@@ -23,8 +24,11 @@ func (i *IdentityCache) notifyUpdated() error {
 	return i.repoCache.identityUpdated(i.Identity.Id())
 }
 
-func (i *IdentityCache) Mutate(f func(identity.Mutator) identity.Mutator) error {
-	i.Identity.Mutate(f)
+func (i *IdentityCache) Mutate(repo repository.RepoClock, f func(*identity.Mutator)) error {
+	err := i.Identity.Mutate(repo, f)
+	if err != nil {
+		return err
+	}
 	return i.notifyUpdated()
 }
 

cache/repo_cache.go 🔗

@@ -18,7 +18,8 @@ import (
 // 1: original format
 // 2: added cache for identities with a reference in the bug cache
 // 3: no more legacy identity
-const formatVersion = 3
+// 4: entities make their IDs from data, not git commit
+const formatVersion = 4
 
 // The maximum number of bugs loaded in memory. After that, eviction will be done.
 const defaultMaxLoadedBugs = 1000

cache/repo_cache_identity.go 🔗

@@ -225,17 +225,20 @@ func (c *RepoCache) NewIdentityFromGitUserRaw(metadata map[string]string) (*Iden
 // NewIdentity create a new identity
 // The new identity is written in the repository (commit)
 func (c *RepoCache) NewIdentity(name string, email string) (*IdentityCache, error) {
-	return c.NewIdentityRaw(name, email, "", "", nil)
+	return c.NewIdentityRaw(name, email, "", "", nil, nil)
 }
 
 // NewIdentityFull create a new identity
 // The new identity is written in the repository (commit)
-func (c *RepoCache) NewIdentityFull(name string, email string, login string, avatarUrl string) (*IdentityCache, error) {
-	return c.NewIdentityRaw(name, email, login, avatarUrl, nil)
+func (c *RepoCache) NewIdentityFull(name string, email string, login string, avatarUrl string, keys []*identity.Key) (*IdentityCache, error) {
+	return c.NewIdentityRaw(name, email, login, avatarUrl, keys, nil)
 }
 
-func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avatarUrl string, metadata map[string]string) (*IdentityCache, error) {
-	i := identity.NewIdentityFull(name, email, login, avatarUrl)
+func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avatarUrl string, keys []*identity.Key, metadata map[string]string) (*IdentityCache, error) {
+	i, err := identity.NewIdentityFull(c.repo, name, email, login, avatarUrl, keys)
+	if err != nil {
+		return nil, err
+	}
 	return c.finishIdentity(i, metadata)
 }
 

commands/user.go 🔗

@@ -35,7 +35,7 @@ func newUserCommand() *cobra.Command {
 	flags.SortFlags = false
 
 	flags.StringVarP(&options.fields, "field", "f", "",
-		"Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]")
+		"Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name]")
 
 	return cmd
 }
@@ -71,7 +71,9 @@ func runUser(env *Env, opts userOptions, args []string) error {
 			env.out.Printf("%s\n", id.LastModification().
 				Time().Format("Mon Jan 2 15:04:05 2006 +0200"))
 		case "lastModificationLamport":
-			env.out.Printf("%d\n", id.LastModificationLamport())
+			for name, t := range id.LastModificationLamports() {
+				env.out.Printf("%s\n%d\n", name, t)
+			}
 		case "metadata":
 			for key, value := range id.ImmutableMetadata() {
 				env.out.Printf("%s\n%s\n", key, value)
@@ -90,9 +92,11 @@ func runUser(env *Env, opts userOptions, args []string) error {
 	env.out.Printf("Name: %s\n", id.Name())
 	env.out.Printf("Email: %s\n", id.Email())
 	env.out.Printf("Login: %s\n", id.Login())
-	env.out.Printf("Last modification: %s (lamport %d)\n",
-		id.LastModification().Time().Format("Mon Jan 2 15:04:05 2006 +0200"),
-		id.LastModificationLamport())
+	env.out.Printf("Last modification: %s\n", id.LastModification().Time().Format("Mon Jan 2 15:04:05 2006 +0200"))
+	env.out.Printf("Last moditication (lamport):\n")
+	for name, t := range id.LastModificationLamports() {
+		env.out.Printf("\t%s: %d", name, t)
+	}
 	env.out.Println("Metadata:")
 	for key, value := range id.ImmutableMetadata() {
 		env.out.Printf("    %s --> %s\n", key, value)

commands/user_create.go 🔗

@@ -48,7 +48,7 @@ func runUserCreate(env *Env) error {
 		return err
 	}
 
-	id, err := env.backend.NewIdentityRaw(name, email, "", avatarURL, nil)
+	id, err := env.backend.NewIdentityRaw(name, email, "", avatarURL, nil, nil)
 	if err != nil {
 		return err
 	}

doc/man/git-bug-user.1 🔗

@@ -19,7 +19,7 @@ Display or change the user identity.
 .SH OPTIONS
 .PP
 \fB\-f\fP, \fB\-\-field\fP=""
-	Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]
+	Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name]
 
 .PP
 \fB\-h\fP, \fB\-\-help\fP[=false]

doc/md/git-bug_user.md 🔗

@@ -9,7 +9,7 @@ git-bug user [USER-ID] [flags]
 ### Options
 
 ```
-  -f, --field string   Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]
+  -f, --field string   Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name]
   -h, --help           help for user
 ```
 

entity/id.go 🔗

@@ -8,8 +8,8 @@ import (
 	"github.com/pkg/errors"
 )
 
-const IdLengthSHA1 = 40
-const IdLengthSHA256 = 64
+// sha-256
+const idLength = 64
 const humanIdLength = 7
 
 const UnsetId = Id("unset")
@@ -55,7 +55,7 @@ func (i Id) MarshalGQL(w io.Writer) {
 
 // IsValid tell if the Id is valid
 func (i Id) Validate() error {
-	if len(i) != IdLengthSHA1 && len(i) != IdLengthSHA256 {
+	if len(i) != idLength {
 		return fmt.Errorf("invalid length")
 	}
 	for _, r := range i {

entity/interface.go 🔗

@@ -2,5 +2,11 @@ package entity
 
 type Interface interface {
 	// Id return the Entity identifier
+	//
+	// This Id need to be immutable without having to store the entity somewhere (ie, an entity only in memory
+	// should have a valid Id, and it should not change if further edit are done on this entity).
+	// How to achieve that is up to the entity itself. A common way would be to take a hash of an immutable data at
+	// the root of the entity.
+	// It is acceptable to use such a hash and keep mutating that data as long as Id() is not called.
 	Id() Id
 }

go.sum 🔗

@@ -727,6 +727,7 @@ golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d h1:W07d4xkoAUSNOkOzdzXCdFGxT7o2rW4q8M34tB2i//k=
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

misc/powershell_completion/git-bug 🔗

@@ -212,8 +212,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
             break
         }
         'git-bug;user' {
-            [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]')
-            [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]')
+            [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name]')
+            [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name]')
             [CompletionResult]::new('adopt', 'adopt', [CompletionResultType]::ParameterValue, 'Adopt an existing identity as your own.')
             [CompletionResult]::new('create', 'create', [CompletionResultType]::ParameterValue, 'Create a new identity.')
             [CompletionResult]::new('ls', 'ls', [CompletionResultType]::ParameterValue, 'List identities.')

misc/random_bugs/create_random_bugs.go 🔗

@@ -157,8 +157,8 @@ func GenerateRandomOperationPacksWithSeed(packNumber int, opNumber int, seed int
 	return result
 }
 
-func person() *identity.Identity {
-	return identity.NewIdentity(fake.FullName(), fake.EmailAddress())
+func person(repo repository.RepoClock) (*identity.Identity, error) {
+	return identity.NewIdentity(repo, fake.FullName(), fake.EmailAddress())
 }
 
 var persons []*identity.Identity
@@ -166,8 +166,11 @@ var persons []*identity.Identity
 func generateRandomPersons(repo repository.ClockedRepo, n int) {
 	persons = make([]*identity.Identity, n)
 	for i := range persons {
-		p := person()
-		err := p.Commit(repo)
+		p, err := person(repo)
+		if err != nil {
+			panic(err)
+		}
+		err = p.Commit(repo)
 		if err != nil {
 			panic(err)
 		}