From 71f9290fdae7551f3d3ada2179ece4084304d734 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 19 Feb 2019 00:19:27 +0100 Subject: identity: store the times properly --- bug/operation_pack.go | 2 +- cache/repo_cache.go | 2 +- commands/user.go | 2 -- identity/bare.go | 11 +++++++++-- identity/identity.go | 13 +++++++++---- identity/identity_stub.go | 4 ++-- identity/interface.go | 10 +++++++--- identity/version.go | 19 +++++++++++++------ repository/git.go | 12 ++++++++++++ repository/mock_repo.go | 10 ++++++++++ repository/repo.go | 7 +++++++ 11 files changed, 71 insertions(+), 21 deletions(-) diff --git a/bug/operation_pack.go b/bug/operation_pack.go index 55fc018e..5f3e9da8 100644 --- a/bug/operation_pack.go +++ b/bug/operation_pack.go @@ -138,7 +138,7 @@ func (opp *OperationPack) Validate() error { // Write will serialize and store the OperationPack as a git blob and return // its hash -func (opp *OperationPack) Write(repo repository.Repo) (git.Hash, error) { +func (opp *OperationPack) Write(repo repository.ClockedRepo) (git.Hash, error) { // make sure we don't write invalid data err := opp.Validate() if err != nil { diff --git a/cache/repo_cache.go b/cache/repo_cache.go index d5768125..e87119fe 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -712,7 +712,7 @@ func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*IdentityCache, error) // preallocate but empty matching := make([]string, 0, 5) - for id := range c.identities { + for id := range c.identitiesExcerpts { if strings.HasPrefix(id, prefix) { matching = append(matching, id) } diff --git a/commands/user.go b/commands/user.go index e7a1da63..64482555 100644 --- a/commands/user.go +++ b/commands/user.go @@ -23,8 +23,6 @@ func runUser(cmd *cobra.Command, args []string) error { var id *cache.IdentityCache if len(args) == 1 { - // TODO - return errors.New("this is not working yet, cache need to be hacked on") id, err = backend.ResolveIdentityPrefix(args[0]) } else { id, err = backend.GetUserIdentity() diff --git a/identity/bare.go b/identity/bare.go index c54277a0..d3f7655a 100644 --- a/identity/bare.go +++ b/identity/bare.go @@ -65,6 +65,7 @@ func (i *Bare) UnmarshalJSON(data []byte) error { return nil } +// Id return the Identity identifier func (i *Bare) Id() string { // We don't have a proper ID at hand, so let's hash all the data to get one. // Hopefully the @@ -84,18 +85,22 @@ func (i *Bare) Id() string { return i.id } +// Name return the last version of the name func (i *Bare) Name() string { return i.name } +// Email return the last version of the email func (i *Bare) Email() string { return i.email } +// Login return the last version of the login func (i *Bare) Login() string { return i.login } +// AvatarUrl return the last version of the Avatar URL func (i *Bare) AvatarUrl() string { return i.avatarUrl } @@ -164,12 +169,14 @@ func (i *Bare) Validate() error { // Write the identity into the Repository. In particular, this ensure that // the Id is properly set. -func (i *Bare) Commit(repo repository.Repo) error { +func (i *Bare) Commit(repo repository.ClockedRepo) error { // Nothing to do, everything is directly embedded return nil } -func (i *Bare) CommitAsNeeded(repo repository.Repo) error { +// If needed, write the identity into the Repository. In particular, this +// ensure that the Id is properly set. +func (i *Bare) CommitAsNeeded(repo repository.ClockedRepo) error { // Nothing to do, everything is directly embedded return nil } diff --git a/identity/identity.go b/identity/identity.go index 193a3013..b9d40967 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "strings" + "time" "github.com/pkg/errors" @@ -252,8 +253,8 @@ func (i *Identity) AddVersion(version *Version) { // Write the identity into the Repository. In particular, this ensure that // the Id is properly set. -func (i *Identity) Commit(repo repository.Repo) error { - // Todo: check for mismatch between memory and commited data +func (i *Identity) Commit(repo repository.ClockedRepo) error { + // Todo: check for mismatch between memory and commit data if !i.NeedCommit() { return fmt.Errorf("can't commit an identity with no pending version") @@ -266,10 +267,14 @@ func (i *Identity) Commit(repo repository.Repo) error { for _, v := range i.versions { if v.commitHash != "" { i.lastCommit = v.commitHash - // ignore already commited versions + // ignore already commit versions continue } + // get the times where new versions starts to be valid + v.time = repo.EditTime() + v.unixTime = time.Now().Unix() + blobHash, err := v.Write(repo) if err != nil { return err @@ -319,7 +324,7 @@ func (i *Identity) Commit(repo repository.Repo) error { return nil } -func (i *Identity) CommitAsNeeded(repo repository.Repo) error { +func (i *Identity) CommitAsNeeded(repo repository.ClockedRepo) error { if !i.NeedCommit() { return nil } diff --git a/identity/identity_stub.go b/identity/identity_stub.go index e91600b0..830cfb99 100644 --- a/identity/identity_stub.go +++ b/identity/identity_stub.go @@ -76,11 +76,11 @@ func (IdentityStub) Validate() error { panic("identities needs to be properly loaded with identity.ReadLocal()") } -func (IdentityStub) Commit(repo repository.Repo) error { +func (IdentityStub) Commit(repo repository.ClockedRepo) error { panic("identities needs to be properly loaded with identity.ReadLocal()") } -func (i *IdentityStub) CommitAsNeeded(repo repository.Repo) error { +func (i *IdentityStub) CommitAsNeeded(repo repository.ClockedRepo) error { panic("identities needs to be properly loaded with identity.ReadLocal()") } diff --git a/identity/interface.go b/identity/interface.go index 55877c02..9fe4db4f 100644 --- a/identity/interface.go +++ b/identity/interface.go @@ -6,13 +6,17 @@ import ( ) type Interface interface { + // Id return the Identity identifier Id() string + // Name return the last version of the name Name() string + // Email return the last version of the email Email() string + // Login return the last version of the login Login() string + // AvatarUrl return the last version of the Avatar URL AvatarUrl() string - // Keys return the last version of the valid keys Keys() []Key @@ -28,11 +32,11 @@ type Interface interface { // Write the identity into the Repository. In particular, this ensure that // the Id is properly set. - Commit(repo repository.Repo) error + Commit(repo repository.ClockedRepo) error // If needed, write the identity into the Repository. In particular, this // ensure that the Id is properly set. - CommitAsNeeded(repo repository.Repo) error + CommitAsNeeded(repo repository.ClockedRepo) error // IsProtected return true if the chain of git commits started to be signed. // If that's the case, only signed commit with a valid key for this identity can be added. diff --git a/identity/version.go b/identity/version.go index 90bf83f2..6ffffd99 100644 --- a/identity/version.go +++ b/identity/version.go @@ -17,14 +17,11 @@ const formatVersion = 1 // Version is a complete set of information about an Identity at a point in time. type Version struct { - // Not serialized - commitHash git.Hash - - // Todo: add unix timestamp for ordering with identical lamport time ? - // The lamport time at which this version become effective // The reference time is the bug edition lamport clock - time lamport.Time + // It must be the first field in this struct due to https://github.com/golang/go/issues/599 + time lamport.Time + unixTime int64 name string email string @@ -43,6 +40,9 @@ type Version struct { // A set of arbitrary key/value to store metadata about a version or about an Identity in general. metadata map[string]string + + // Not serialized + commitHash git.Hash } type VersionJSON struct { @@ -50,6 +50,7 @@ type VersionJSON struct { FormatVersion uint `json:"version"` Time lamport.Time `json:"time"` + UnixTime int64 `json:"unix_time"` Name string `json:"name"` Email string `json:"email"` Login string `json:"login"` @@ -63,6 +64,7 @@ func (v *Version) MarshalJSON() ([]byte, error) { return json.Marshal(VersionJSON{ FormatVersion: formatVersion, Time: v.time, + UnixTime: v.unixTime, Name: v.name, Email: v.email, Login: v.login, @@ -85,6 +87,7 @@ func (v *Version) UnmarshalJSON(data []byte) error { } v.time = aux.Time + v.unixTime = aux.UnixTime v.name = aux.Name v.email = aux.Email v.login = aux.Login @@ -97,6 +100,10 @@ func (v *Version) UnmarshalJSON(data []byte) error { } func (v *Version) Validate() error { + if v.unixTime == 0 { + return fmt.Errorf("unix time not set") + } + if text.Empty(v.name) && text.Empty(v.login) { return fmt.Errorf("either name or login should be set") } diff --git a/repository/git.go b/repository/git.go index 10fddac3..836de8f0 100644 --- a/repository/git.go +++ b/repository/git.go @@ -20,6 +20,8 @@ const editClockFile = "/.git/git-bug/edit-clock" // ErrNotARepo is the error returned when the git repo root wan't be found var ErrNotARepo = errors.New("not a git repository") +var _ ClockedRepo = &GitRepo{} + // GitRepo represents an instance of a (local) git repository. type GitRepo struct { Path string @@ -440,11 +442,21 @@ func (repo *GitRepo) WriteClocks() error { return nil } +// CreateTime return the current value of the creation clock +func (repo *GitRepo) CreateTime() lamport.Time { + return repo.createClock.Time() +} + // CreateTimeIncrement increment the creation clock and return the new value. func (repo *GitRepo) CreateTimeIncrement() (lamport.Time, error) { return repo.createClock.Increment() } +// EditTime return the current value of the edit clock +func (repo *GitRepo) EditTime() lamport.Time { + return repo.editClock.Time() +} + // EditTimeIncrement increment the edit clock and return the new value. func (repo *GitRepo) EditTimeIncrement() (lamport.Time, error) { return repo.editClock.Increment() diff --git a/repository/mock_repo.go b/repository/mock_repo.go index 74de8f57..97a4504f 100644 --- a/repository/mock_repo.go +++ b/repository/mock_repo.go @@ -9,6 +9,8 @@ import ( "github.com/MichaelMure/git-bug/util/lamport" ) +var _ ClockedRepo = &mockRepoForTest{} + // mockRepoForTest defines an instance of Repo that can be used for testing. type mockRepoForTest struct { config map[string]string @@ -227,10 +229,18 @@ func (r *mockRepoForTest) WriteClocks() error { return nil } +func (r *mockRepoForTest) CreateTime() lamport.Time { + return r.createClock.Time() +} + func (r *mockRepoForTest) CreateTimeIncrement() (lamport.Time, error) { return r.createClock.Increment(), nil } +func (r *mockRepoForTest) EditTime() lamport.Time { + return r.editClock.Time() +} + func (r *mockRepoForTest) EditTimeIncrement() (lamport.Time, error) { return r.editClock.Increment(), nil } diff --git a/repository/repo.go b/repository/repo.go index 100feaed..8a66c320 100644 --- a/repository/repo.go +++ b/repository/repo.go @@ -83,6 +83,7 @@ type Repo interface { GetTreeHash(commit git.Hash) (git.Hash, error) } +// ClockedRepo is a Repo that also has Lamport clocks type ClockedRepo interface { Repo @@ -92,9 +93,15 @@ type ClockedRepo interface { // WriteClocks write the clocks values into the repo WriteClocks() error + // CreateTime return the current value of the creation clock + CreateTime() lamport.Time + // CreateTimeIncrement increment the creation clock and return the new value. CreateTimeIncrement() (lamport.Time, error) + // EditTime return the current value of the edit clock + EditTime() lamport.Time + // EditTimeIncrement increment the edit clock and return the new value. EditTimeIncrement() (lamport.Time, error) -- cgit