fork of go-git with some jj specific features
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 239 lines 6.0 kB view raw
1// Package object contains implementations of all Git objects and utility 2// functions to work with them. 3package object 4 5import ( 6 "bytes" 7 "errors" 8 "fmt" 9 "io" 10 "strconv" 11 "time" 12 13 "github.com/go-git/go-git/v5/plumbing" 14 "github.com/go-git/go-git/v5/plumbing/storer" 15) 16 17// ErrUnsupportedObject trigger when a non-supported object is being decoded. 18var ErrUnsupportedObject = errors.New("unsupported object type") 19 20// Object is a generic representation of any git object. It is implemented by 21// Commit, Tree, Blob, and Tag, and includes the functions that are common to 22// them. 23// 24// Object is returned when an object can be of any type. It is frequently used 25// with a type cast to acquire the specific type of object: 26// 27// func process(obj Object) { 28// switch o := obj.(type) { 29// case *Commit: 30// // o is a Commit 31// case *Tree: 32// // o is a Tree 33// case *Blob: 34// // o is a Blob 35// case *Tag: 36// // o is a Tag 37// } 38// } 39// 40// This interface is intentionally different from plumbing.EncodedObject, which 41// is a lower level interface used by storage implementations to read and write 42// objects in its encoded form. 43type Object interface { 44 ID() plumbing.Hash 45 Type() plumbing.ObjectType 46 Decode(plumbing.EncodedObject) error 47 Encode(plumbing.EncodedObject) error 48} 49 50// GetObject gets an object from an object storer and decodes it. 51func GetObject(s storer.EncodedObjectStorer, h plumbing.Hash) (Object, error) { 52 o, err := s.EncodedObject(plumbing.AnyObject, h) 53 if err != nil { 54 return nil, err 55 } 56 57 return DecodeObject(s, o) 58} 59 60// DecodeObject decodes an encoded object into an Object and associates it to 61// the given object storer. 62func DecodeObject(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (Object, error) { 63 switch o.Type() { 64 case plumbing.CommitObject: 65 return DecodeCommit(s, o) 66 case plumbing.TreeObject: 67 return DecodeTree(s, o) 68 case plumbing.BlobObject: 69 return DecodeBlob(o) 70 case plumbing.TagObject: 71 return DecodeTag(s, o) 72 default: 73 return nil, plumbing.ErrInvalidType 74 } 75} 76 77// DateFormat is the format being used in the original git implementation 78const DateFormat = "Mon Jan 02 15:04:05 2006 -0700" 79 80// Signature is used to identify who and when created a commit or tag. 81type Signature struct { 82 // Name represents a person name. It is an arbitrary string. 83 Name string 84 // Email is an email, but it cannot be assumed to be well-formed. 85 Email string 86 // When is the timestamp of the signature. 87 When time.Time 88} 89 90// Decode decodes a byte slice into a signature 91func (s *Signature) Decode(b []byte) { 92 open := bytes.LastIndexByte(b, '<') 93 close := bytes.LastIndexByte(b, '>') 94 if open == -1 || close == -1 { 95 return 96 } 97 98 if close < open { 99 return 100 } 101 102 s.Name = string(bytes.Trim(b[:open], " ")) 103 s.Email = string(b[open+1 : close]) 104 105 hasTime := close+2 < len(b) 106 if hasTime { 107 s.decodeTimeAndTimeZone(b[close+2:]) 108 } 109} 110 111// Encode encodes a Signature into a writer. 112func (s *Signature) Encode(w io.Writer) error { 113 if _, err := fmt.Fprintf(w, "%s <%s> ", s.Name, s.Email); err != nil { 114 return err 115 } 116 if err := s.encodeTimeAndTimeZone(w); err != nil { 117 return err 118 } 119 return nil 120} 121 122var timeZoneLength = 5 123 124func (s *Signature) decodeTimeAndTimeZone(b []byte) { 125 space := bytes.IndexByte(b, ' ') 126 if space == -1 { 127 space = len(b) 128 } 129 130 ts, err := strconv.ParseInt(string(b[:space]), 10, 64) 131 if err != nil { 132 return 133 } 134 135 s.When = time.Unix(ts, 0).In(time.UTC) 136 var tzStart = space + 1 137 if tzStart >= len(b) || tzStart+timeZoneLength > len(b) { 138 return 139 } 140 141 timezone := string(b[tzStart : tzStart+timeZoneLength]) 142 tzhours, err1 := strconv.ParseInt(timezone[0:3], 10, 64) 143 tzmins, err2 := strconv.ParseInt(timezone[3:], 10, 64) 144 if err1 != nil || err2 != nil { 145 return 146 } 147 if tzhours < 0 { 148 tzmins *= -1 149 } 150 151 tz := time.FixedZone("", int(tzhours*60*60+tzmins*60)) 152 153 s.When = s.When.In(tz) 154} 155 156func (s *Signature) encodeTimeAndTimeZone(w io.Writer) error { 157 u := s.When.Unix() 158 if u < 0 { 159 u = 0 160 } 161 _, err := fmt.Fprintf(w, "%d %s", u, s.When.Format("-0700")) 162 return err 163} 164 165func (s *Signature) String() string { 166 return fmt.Sprintf("%s <%s>", s.Name, s.Email) 167} 168 169// ObjectIter provides an iterator for a set of objects. 170type ObjectIter struct { 171 storer.EncodedObjectIter 172 s storer.EncodedObjectStorer 173} 174 175// NewObjectIter takes a storer.EncodedObjectStorer and a 176// storer.EncodedObjectIter and returns an *ObjectIter that iterates over all 177// objects contained in the storer.EncodedObjectIter. 178func NewObjectIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *ObjectIter { 179 return &ObjectIter{iter, s} 180} 181 182// Next moves the iterator to the next object and returns a pointer to it. If 183// there are no more objects, it returns io.EOF. 184func (iter *ObjectIter) Next() (Object, error) { 185 for { 186 obj, err := iter.EncodedObjectIter.Next() 187 if err != nil { 188 return nil, err 189 } 190 191 o, err := iter.toObject(obj) 192 if err == plumbing.ErrInvalidType { 193 continue 194 } 195 196 if err != nil { 197 return nil, err 198 } 199 200 return o, nil 201 } 202} 203 204// ForEach call the cb function for each object contained on this iter until 205// an error happens or the end of the iter is reached. If ErrStop is sent 206// the iteration is stop but no error is returned. The iterator is closed. 207func (iter *ObjectIter) ForEach(cb func(Object) error) error { 208 return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { 209 o, err := iter.toObject(obj) 210 if err == plumbing.ErrInvalidType { 211 return nil 212 } 213 214 if err != nil { 215 return err 216 } 217 218 return cb(o) 219 }) 220} 221 222func (iter *ObjectIter) toObject(obj plumbing.EncodedObject) (Object, error) { 223 switch obj.Type() { 224 case plumbing.BlobObject: 225 blob := &Blob{} 226 return blob, blob.Decode(obj) 227 case plumbing.TreeObject: 228 tree := &Tree{s: iter.s} 229 return tree, tree.Decode(obj) 230 case plumbing.CommitObject: 231 commit := &Commit{} 232 return commit, commit.Decode(obj) 233 case plumbing.TagObject: 234 tag := &Tag{} 235 return tag, tag.Decode(obj) 236 default: 237 return nil, plumbing.ErrInvalidType 238 } 239}