Detailed changes
  
  
    
    @@ -3,6 +3,7 @@ package bug
 import (
 	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 	"github.com/dustin/go-humanize"
 )
 
@@ -14,7 +15,7 @@ type Comment struct {
 
 	// Creation time of the comment.
 	// Should be used only for human display, never for ordering as we can't rely on it in a distributed system.
-	UnixTime Timestamp
+	UnixTime timestamp.Timestamp
 }
 
 // FormatTimeRel format the UnixTime of the comment for human consumption
  
  
  
    
    @@ -7,6 +7,7 @@ import (
 	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
 	"github.com/MichaelMure/git-bug/util/text"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 )
 
 var _ Operation = &AddCommentOperation{}
@@ -32,7 +33,7 @@ func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
 		Message:  op.Message,
 		Author:   op.Author,
 		Files:    op.Files,
-		UnixTime: Timestamp(op.UnixTime),
+		UnixTime: timestamp.Timestamp(op.UnixTime),
 	}
 
 	snapshot.Comments = append(snapshot.Comments, comment)
  
  
  
    
    @@ -8,6 +8,7 @@ import (
 	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
 	"github.com/MichaelMure/git-bug/util/text"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 )
 
 var _ Operation = &CreateOperation{}
@@ -34,7 +35,7 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) {
 	comment := Comment{
 		Message:  op.Message,
 		Author:   op.Author,
-		UnixTime: Timestamp(op.UnixTime),
+		UnixTime: timestamp.Timestamp(op.UnixTime),
 	}
 
 	snapshot.Comments = []Comment{comment}
  
  
  
    
    @@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/MichaelMure/git-bug/identity"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -22,7 +23,7 @@ func TestCreate(t *testing.T) {
 	hash, err := create.Hash()
 	assert.NoError(t, err)
 
-	comment := Comment{Author: rene, Message: "message", UnixTime: Timestamp(create.UnixTime)}
+	comment := Comment{Author: rene, Message: "message", UnixTime: timestamp.Timestamp(create.UnixTime)}
 
 	expected := Snapshot{
 		Title: "title",
  
  
  
    
    @@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	"github.com/MichaelMure/git-bug/identity"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 
 	"github.com/MichaelMure/git-bug/util/git"
 	"github.com/MichaelMure/git-bug/util/text"
@@ -58,7 +59,7 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
 	comment := Comment{
 		Message:  op.Message,
 		Files:    op.Files,
-		UnixTime: Timestamp(op.UnixTime),
+		UnixTime: timestamp.Timestamp(op.UnixTime),
 	}
 
 	switch target.(type) {
  
  
  
    
    @@ -6,6 +6,7 @@ import (
 	"sort"
 
 	"github.com/MichaelMure/git-bug/identity"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 
 	"github.com/MichaelMure/git-bug/util/git"
 	"github.com/pkg/errors"
@@ -68,7 +69,7 @@ AddLoop:
 	item := &LabelChangeTimelineItem{
 		hash:     hash,
 		Author:   op.Author,
-		UnixTime: Timestamp(op.UnixTime),
+		UnixTime: timestamp.Timestamp(op.UnixTime),
 		Added:    op.Added,
 		Removed:  op.Removed,
 	}
@@ -162,7 +163,7 @@ func NewLabelChangeOperation(author identity.Interface, unixTime int64, added, r
 type LabelChangeTimelineItem struct {
 	hash     git.Hash
 	Author   identity.Interface
-	UnixTime Timestamp
+	UnixTime timestamp.Timestamp
 	Added    []Label
 	Removed  []Label
 }
  
  
  
    
    @@ -11,7 +11,7 @@ var _ Operation = &NoOpOperation{}
 
 // NoOpOperation is an operation that does not change the bug state. It can
 // however be used to store arbitrary metadata in the bug history, for example
-// to support a bridge feature
+// to support a bridge feature.
 type NoOpOperation struct {
 	OpBase
 }
  
  
  
    
    @@ -2,6 +2,7 @@ package bug
 
 import (
 	"encoding/json"
+	"fmt"
 
 	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
@@ -55,6 +56,10 @@ func (op *SetMetadataOperation) Validate() error {
 		return err
 	}
 
+	if !op.Target.IsValid() {
+		return fmt.Errorf("target hash is invalid")
+	}
+
 	return nil
 }
 
  
  
  
    
    @@ -5,6 +5,7 @@ import (
 
 	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 	"github.com/pkg/errors"
 )
 
@@ -37,7 +38,7 @@ func (op *SetStatusOperation) Apply(snapshot *Snapshot) {
 	item := &SetStatusTimelineItem{
 		hash:     hash,
 		Author:   op.Author,
-		UnixTime: Timestamp(op.UnixTime),
+		UnixTime: timestamp.Timestamp(op.UnixTime),
 		Status:   op.Status,
 	}
 
@@ -114,7 +115,7 @@ func NewSetStatusOp(author identity.Interface, unixTime int64, status Status) *S
 type SetStatusTimelineItem struct {
 	hash     git.Hash
 	Author   identity.Interface
-	UnixTime Timestamp
+	UnixTime timestamp.Timestamp
 	Status   Status
 }
 
  
  
  
    
    @@ -6,6 +6,7 @@ import (
 	"strings"
 
 	"github.com/MichaelMure/git-bug/identity"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 
 	"github.com/MichaelMure/git-bug/util/git"
 	"github.com/MichaelMure/git-bug/util/text"
@@ -41,7 +42,7 @@ func (op *SetTitleOperation) Apply(snapshot *Snapshot) {
 	item := &SetTitleTimelineItem{
 		hash:     hash,
 		Author:   op.Author,
-		UnixTime: Timestamp(op.UnixTime),
+		UnixTime: timestamp.Timestamp(op.UnixTime),
 		Title:    op.Title,
 		Was:      op.Was,
 	}
@@ -139,7 +140,7 @@ func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was
 type SetTitleTimelineItem struct {
 	hash     git.Hash
 	Author   identity.Interface
-	UnixTime Timestamp
+	UnixTime timestamp.Timestamp
 	Title    string
 	Was      string
 }
  
  
  
    
    @@ -5,6 +5,7 @@ import (
 
 	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 )
 
 type TimelineItem interface {
@@ -19,7 +20,7 @@ type CommentHistoryStep struct {
 	Author identity.Interface
 	// The new message
 	Message  string
-	UnixTime Timestamp
+	UnixTime timestamp.Timestamp
 }
 
 // CommentTimelineItem is a TimelineItem that holds a Comment and its edition history
@@ -28,8 +29,8 @@ type CommentTimelineItem struct {
 	Author    identity.Interface
 	Message   string
 	Files     []git.Hash
-	CreatedAt Timestamp
-	LastEdit  Timestamp
+	CreatedAt timestamp.Timestamp
+	LastEdit  timestamp.Timestamp
 	History   []CommentHistoryStep
 }
 
  
  
  
    
    @@ -36,6 +36,9 @@ func runUser(cmd *cobra.Command, args []string) error {
 	fmt.Printf("Name: %s\n", id.Name())
 	fmt.Printf("Login: %s\n", id.Login())
 	fmt.Printf("Email: %s\n", id.Email())
+	fmt.Printf("Last modification: %s (lamport %d)\n",
+		id.LastModification().Time().Format("Mon Jan 2 15:04:05 2006 +0200"),
+		id.LastModificationLamport())
 	// fmt.Printf("Protected: %v\n", id.IsProtected())
 
 	return nil
  
  
  
    
    @@ -19,10 +19,6 @@ 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
  
  
  
    
    @@ -13,8 +13,7 @@ git-bug user ls [flags]
 ### Options
 
 ```
-  -v, --verbose   Print extra information
-  -h, --help      help for ls
+  -h, --help   help for ls
 ```
 
 ### SEE ALSO
  
  
  
    
    @@ -9,6 +9,7 @@ import (
 	"github.com/MichaelMure/git-bug/repository"
 	"github.com/MichaelMure/git-bug/util/lamport"
 	"github.com/MichaelMure/git-bug/util/text"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 )
 
 var _ Interface = &Bare{}
@@ -191,3 +192,13 @@ func (i *Bare) CommitAsNeeded(repo repository.ClockedRepo) error {
 func (i *Bare) IsProtected() bool {
 	return false
 }
+
+// LastModificationLamportTime return the Lamport time at which the last version of the identity became valid.
+func (i *Bare) LastModificationLamport() lamport.Time {
+	return 0
+}
+
+// LastModification return the timestamp at which the last version of the identity became valid.
+func (i *Bare) LastModification() timestamp.Timestamp {
+	return 0
+}
  
  
  
    
    @@ -8,6 +8,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/MichaelMure/git-bug/util/timestamp"
 	"github.com/pkg/errors"
 
 	"github.com/MichaelMure/git-bug/repository"
@@ -33,10 +34,11 @@ type Identity struct {
 	// Id used as unique identifier
 	id string
 
-	lastCommit git.Hash
-
 	// all the successive version of the identity
 	versions []*Version
+
+	// not serialized
+	lastCommit git.Hash
 }
 
 func NewIdentity(name string, email string) *Identity {
@@ -498,13 +500,6 @@ func (i *Identity) Keys() []Key {
 	return i.lastVersion().keys
 }
 
-// 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.
-func (i *Identity) IsProtected() bool {
-	// Todo
-	return false
-}
-
 // ValidKeysAtTime return the set of keys valid at a given lamport time
 func (i *Identity) ValidKeysAtTime(time lamport.Time) []Key {
 	var result []Key
@@ -535,6 +530,23 @@ func (i *Identity) DisplayName() string {
 	panic("invalid person data")
 }
 
+// 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.
+func (i *Identity) IsProtected() bool {
+	// Todo
+	return false
+}
+
+// LastModificationLamportTime return the Lamport time at which the last version of the identity became valid.
+func (i *Identity) LastModificationLamport() lamport.Time {
+	return i.lastVersion().time
+}
+
+// LastModification return the timestamp at which the last version of the identity became valid.
+func (i *Identity) LastModification() timestamp.Timestamp {
+	return timestamp.Timestamp(i.lastVersion().unixTime)
+}
+
 // SetMetadata store arbitrary metadata along the last defined Version.
 // If the Version has been commit to git already, it won't be overwritten.
 func (i *Identity) SetMetadata(key string, value string) {
  
  
  
    
    @@ -5,6 +5,7 @@ import (
 
 	"github.com/MichaelMure/git-bug/repository"
 	"github.com/MichaelMure/git-bug/util/lamport"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 )
 
 var _ Interface = &IdentityStub{}
@@ -93,3 +94,11 @@ func (i *IdentityStub) CommitAsNeeded(repo repository.ClockedRepo) error {
 func (IdentityStub) IsProtected() bool {
 	panic("identities needs to be properly loaded with identity.ReadLocal()")
 }
+
+func (i *IdentityStub) LastModificationLamport() lamport.Time {
+	panic("identities needs to be properly loaded with identity.ReadLocal()")
+}
+
+func (i *IdentityStub) LastModification() timestamp.Timestamp {
+	panic("identities needs to be properly loaded with identity.ReadLocal()")
+}
  
  
  
    
    @@ -3,6 +3,7 @@ package identity
 import (
 	"github.com/MichaelMure/git-bug/repository"
 	"github.com/MichaelMure/git-bug/util/lamport"
+	"github.com/MichaelMure/git-bug/util/timestamp"
 )
 
 type Interface interface {
@@ -48,4 +49,10 @@ type Interface interface {
 	// 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.
 	IsProtected() bool
+
+	// LastModificationLamportTime return the Lamport time at which the last version of the identity became valid.
+	LastModificationLamport() lamport.Time
+
+	// LastModification return the timestamp at which the last version of the identity became valid.
+	LastModification() timestamp.Timestamp
 }
  
  
  
    
    @@ -853,9 +853,6 @@ _git-bug_user_ls()
     flags_with_completion=()
     flags_completion=()
 
-    flags+=("--verbose")
-    flags+=("-v")
-    local_nonpersistent_flags+=("--verbose")
 
     must_have_one_flag=()
     must_have_one_noun=()
  
  
  
    
    @@ -17,15 +17,6 @@ case $state in
   ;;
   level2)
     case $words[2] in
-      bridge)
-        _arguments '2: :(configure pull rm)'
-      ;;
-      comment)
-        _arguments '2: :(add)'
-      ;;
-      label)
-        _arguments '2: :(add rm)'
-      ;;
       status)
         _arguments '2: :(close open)'
       ;;
@@ -35,6 +26,15 @@ case $state in
       user)
         _arguments '2: :(adopt create ls)'
       ;;
+      bridge)
+        _arguments '2: :(configure pull rm)'
+      ;;
+      comment)
+        _arguments '2: :(add)'
+      ;;
+      label)
+        _arguments '2: :(add rm)'
+      ;;
       *)
         _arguments '*: :_files'
       ;;
  
  
  
    
    @@ -1,4 +1,4 @@
-package bug
+package timestamp
 
 import "time"