本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
6.0KB

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