github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/doltdb/commit_meta.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package doltdb 16 17 import ( 18 "errors" 19 "fmt" 20 "strings" 21 "time" 22 23 "github.com/dolthub/dolt/go/store/types" 24 ) 25 26 const ( 27 commitMetaNameKey = "name" 28 commitMetaEmailKey = "email" 29 commitMetaDescKey = "desc" 30 commitMetaTimestampKey = "timestamp" 31 commitMetaUserTSKey = "user_timestamp" 32 commitMetaVersionKey = "metaversion" 33 34 commitMetaStName = "metadata" 35 commitMetaVersion = "1.0" 36 ) 37 38 var ErrNameNotConfigured = errors.New("Aborting commit due to empty committer name. Is your config set?") 39 var ErrEmailNotConfigured = errors.New("Aborting commit due to empty committer email. Is your config set?") 40 var ErrEmptyCommitMessage = errors.New("Aborting commit due to empty commit message.") 41 42 var CommitNowFunc = time.Now 43 var CommitLoc = time.Local 44 45 // CommitMeta contains all the metadata that is associated with a commit within a data repo. 46 type CommitMeta struct { 47 Name string 48 Email string 49 Timestamp uint64 50 Description string 51 UserTimestamp int64 52 } 53 54 var uMilliToNano = uint64(time.Millisecond / time.Nanosecond) 55 var milliToNano = int64(time.Millisecond / time.Nanosecond) 56 var secToMilli = int64(time.Second / time.Millisecond) 57 58 // NewCommitMeta creates a CommitMeta instance from a name, email, and description and uses the current time for the 59 // timestamp 60 func NewCommitMeta(name, email, desc string) (*CommitMeta, error) { 61 return NewCommitMetaWithUserTS(name, email, desc, CommitNowFunc()) 62 } 63 64 // NewCommitMetaWithUserTS creates a user metadata 65 func NewCommitMetaWithUserTS(name, email, desc string, userTS time.Time) (*CommitMeta, error) { 66 n := strings.TrimSpace(name) 67 e := strings.TrimSpace(email) 68 d := strings.TrimSpace(desc) 69 70 if n == "" { 71 return nil, ErrNameNotConfigured 72 } 73 74 if e == "" { 75 return nil, ErrEmailNotConfigured 76 } 77 78 if d == "" { 79 return nil, ErrEmptyCommitMessage 80 } 81 82 ns := uint64(CommitNowFunc().UnixNano()) 83 ms := ns / uMilliToNano 84 85 userMS := userTS.UnixNano() / milliToNano 86 87 return &CommitMeta{n, e, ms, d, userMS}, nil 88 } 89 90 func getRequiredFromSt(st types.Struct, k string) (types.Value, error) { 91 if v, ok, err := st.MaybeGet(k); err != nil { 92 return nil, err 93 } else if ok { 94 return v, nil 95 } 96 97 return nil, errors.New("Missing required field \"" + k + "\".") 98 } 99 100 func commitMetaFromNomsSt(st types.Struct) (*CommitMeta, error) { 101 e, err := getRequiredFromSt(st, commitMetaEmailKey) 102 103 if err != nil { 104 return nil, err 105 } 106 107 n, err := getRequiredFromSt(st, commitMetaNameKey) 108 109 if err != nil { 110 return nil, err 111 } 112 113 d, err := getRequiredFromSt(st, commitMetaDescKey) 114 115 if err != nil { 116 return nil, err 117 } 118 119 ts, err := getRequiredFromSt(st, commitMetaTimestampKey) 120 121 if err != nil { 122 return nil, err 123 } 124 125 userTS, ok, err := st.MaybeGet(commitMetaUserTSKey) 126 127 if err != nil { 128 return nil, err 129 } else if !ok { 130 userTS = types.Int(int64(uint64(ts.(types.Uint)))) 131 } 132 133 return &CommitMeta{ 134 string(n.(types.String)), 135 string(e.(types.String)), 136 uint64(ts.(types.Uint)), 137 string(d.(types.String)), 138 int64(userTS.(types.Int)), 139 }, nil 140 } 141 142 func (cm *CommitMeta) toNomsStruct(nbf *types.NomsBinFormat) (types.Struct, error) { 143 metadata := types.StructData{ 144 commitMetaNameKey: types.String(cm.Name), 145 commitMetaEmailKey: types.String(cm.Email), 146 commitMetaDescKey: types.String(cm.Description), 147 commitMetaTimestampKey: types.Uint(cm.Timestamp), 148 commitMetaVersionKey: types.String(commitMetaVersion), 149 commitMetaUserTSKey: types.Int(cm.UserTimestamp), 150 } 151 152 return types.NewStruct(nbf, commitMetaStName, metadata) 153 } 154 155 // Time returns the time at which the commit occurred 156 func (cm *CommitMeta) Time() time.Time { 157 seconds := cm.UserTimestamp / secToMilli 158 nanos := (cm.UserTimestamp % secToMilli) * milliToNano 159 return time.Unix(seconds, nanos) 160 } 161 162 // FormatTS takes the internal timestamp and turns it into a human readable string in the time.RubyDate format 163 // which looks like: "Mon Jan 02 15:04:05 -0700 2006" 164 func (cm *CommitMeta) FormatTS() string { 165 return cm.Time().In(CommitLoc).Format(time.RubyDate) 166 } 167 168 // String returns the human readable string representation of the commit data 169 func (cm *CommitMeta) String() string { 170 return fmt.Sprintf("name: %s, email: %s, timestamp: %s, description: %s", cm.Name, cm.Email, cm.FormatTS(), cm.Description) 171 }