git_refs.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // Copyright 2013 The go-github AUTHORS. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package github
  6. import (
  7. "context"
  8. "encoding/json"
  9. "errors"
  10. "fmt"
  11. "strings"
  12. )
  13. // Reference represents a GitHub reference.
  14. type Reference struct {
  15. Ref *string `json:"ref"`
  16. URL *string `json:"url"`
  17. Object *GitObject `json:"object"`
  18. NodeID *string `json:"node_id,omitempty"`
  19. }
  20. func (r Reference) String() string {
  21. return Stringify(r)
  22. }
  23. // GitObject represents a Git object.
  24. type GitObject struct {
  25. Type *string `json:"type"`
  26. SHA *string `json:"sha"`
  27. URL *string `json:"url"`
  28. }
  29. func (o GitObject) String() string {
  30. return Stringify(o)
  31. }
  32. // createRefRequest represents the payload for creating a reference.
  33. type createRefRequest struct {
  34. Ref *string `json:"ref"`
  35. SHA *string `json:"sha"`
  36. }
  37. // updateRefRequest represents the payload for updating a reference.
  38. type updateRefRequest struct {
  39. SHA *string `json:"sha"`
  40. Force *bool `json:"force"`
  41. }
  42. // GetRef fetches a single Reference object for a given Git ref.
  43. // If there is no exact match, GetRef will return an error.
  44. //
  45. // Note: The GitHub API can return multiple matches.
  46. // If you wish to use this functionality please use the GetRefs() method.
  47. //
  48. // GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference
  49. func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref string) (*Reference, *Response, error) {
  50. ref = strings.TrimPrefix(ref, "refs/")
  51. u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref)
  52. req, err := s.client.NewRequest("GET", u, nil)
  53. if err != nil {
  54. return nil, nil, err
  55. }
  56. // TODO: remove custom Accept header when this API fully launches.
  57. req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview)
  58. r := new(Reference)
  59. resp, err := s.client.Do(ctx, req, r)
  60. if _, ok := err.(*json.UnmarshalTypeError); ok {
  61. // Multiple refs, means there wasn't an exact match.
  62. return nil, resp, errors.New("no exact match found for this ref")
  63. } else if err != nil {
  64. return nil, resp, err
  65. }
  66. return r, resp, nil
  67. }
  68. // GetRefs fetches a slice of Reference objects for a given Git ref.
  69. // If there is an exact match, only that ref is returned.
  70. // If there is no exact match, GitHub returns all refs that start with ref.
  71. // If returned error is nil, there will be at least 1 ref returned.
  72. // For example:
  73. //
  74. // "heads/featureA" -> ["refs/heads/featureA"] // Exact match, single ref is returned.
  75. // "heads/feature" -> ["refs/heads/featureA", "refs/heads/featureB"] // All refs that start with ref.
  76. // "heads/notexist" -> [] // Returns an error.
  77. //
  78. // GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference
  79. func (s *GitService) GetRefs(ctx context.Context, owner string, repo string, ref string) ([]*Reference, *Response, error) {
  80. ref = strings.TrimPrefix(ref, "refs/")
  81. u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref)
  82. req, err := s.client.NewRequest("GET", u, nil)
  83. if err != nil {
  84. return nil, nil, err
  85. }
  86. // TODO: remove custom Accept header when this API fully launches.
  87. req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview)
  88. var rawJSON json.RawMessage
  89. resp, err := s.client.Do(ctx, req, &rawJSON)
  90. if err != nil {
  91. return nil, resp, err
  92. }
  93. // Prioritize the most common case: a single returned ref.
  94. r := new(Reference)
  95. singleUnmarshalError := json.Unmarshal(rawJSON, r)
  96. if singleUnmarshalError == nil {
  97. return []*Reference{r}, resp, nil
  98. }
  99. // Attempt to unmarshal multiple refs.
  100. var rs []*Reference
  101. multipleUnmarshalError := json.Unmarshal(rawJSON, &rs)
  102. if multipleUnmarshalError == nil {
  103. if len(rs) == 0 {
  104. return nil, resp, fmt.Errorf("unexpected response from GitHub API: an array of refs with length 0")
  105. }
  106. return rs, resp, nil
  107. }
  108. return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", singleUnmarshalError, multipleUnmarshalError)
  109. }
  110. // ReferenceListOptions specifies optional parameters to the
  111. // GitService.ListRefs method.
  112. type ReferenceListOptions struct {
  113. Type string `url:"-"`
  114. ListOptions
  115. }
  116. // ListRefs lists all refs in a repository.
  117. //
  118. // GitHub API docs: https://developer.github.com/v3/git/refs/#get-all-references
  119. func (s *GitService) ListRefs(ctx context.Context, owner, repo string, opt *ReferenceListOptions) ([]*Reference, *Response, error) {
  120. var u string
  121. if opt != nil && opt.Type != "" {
  122. u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type)
  123. } else {
  124. u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo)
  125. }
  126. u, err := addOptions(u, opt)
  127. if err != nil {
  128. return nil, nil, err
  129. }
  130. req, err := s.client.NewRequest("GET", u, nil)
  131. if err != nil {
  132. return nil, nil, err
  133. }
  134. // TODO: remove custom Accept header when this API fully launches.
  135. req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview)
  136. var rs []*Reference
  137. resp, err := s.client.Do(ctx, req, &rs)
  138. if err != nil {
  139. return nil, resp, err
  140. }
  141. return rs, resp, nil
  142. }
  143. // CreateRef creates a new ref in a repository.
  144. //
  145. // GitHub API docs: https://developer.github.com/v3/git/refs/#create-a-reference
  146. func (s *GitService) CreateRef(ctx context.Context, owner string, repo string, ref *Reference) (*Reference, *Response, error) {
  147. u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo)
  148. req, err := s.client.NewRequest("POST", u, &createRefRequest{
  149. // back-compat with previous behavior that didn't require 'refs/' prefix
  150. Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")),
  151. SHA: ref.Object.SHA,
  152. })
  153. if err != nil {
  154. return nil, nil, err
  155. }
  156. // TODO: remove custom Accept header when this API fully launches.
  157. req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview)
  158. r := new(Reference)
  159. resp, err := s.client.Do(ctx, req, r)
  160. if err != nil {
  161. return nil, resp, err
  162. }
  163. return r, resp, nil
  164. }
  165. // UpdateRef updates an existing ref in a repository.
  166. //
  167. // GitHub API docs: https://developer.github.com/v3/git/refs/#update-a-reference
  168. func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) {
  169. refPath := strings.TrimPrefix(*ref.Ref, "refs/")
  170. u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath)
  171. req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{
  172. SHA: ref.Object.SHA,
  173. Force: &force,
  174. })
  175. if err != nil {
  176. return nil, nil, err
  177. }
  178. // TODO: remove custom Accept header when this API fully launches.
  179. req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview)
  180. r := new(Reference)
  181. resp, err := s.client.Do(ctx, req, r)
  182. if err != nil {
  183. return nil, resp, err
  184. }
  185. return r, resp, nil
  186. }
  187. // DeleteRef deletes a ref from a repository.
  188. //
  189. // GitHub API docs: https://developer.github.com/v3/git/refs/#delete-a-reference
  190. func (s *GitService) DeleteRef(ctx context.Context, owner string, repo string, ref string) (*Response, error) {
  191. ref = strings.TrimPrefix(ref, "refs/")
  192. u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref)
  193. req, err := s.client.NewRequest("DELETE", u, nil)
  194. if err != nil {
  195. return nil, err
  196. }
  197. return s.client.Do(ctx, req, nil)
  198. }