repo.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. Copyright 2015 Google Inc. All rights reserved.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // Package repository contains helper methods for working with a Git repo.
  14. package repository
  15. import (
  16. "crypto/sha1"
  17. "fmt"
  18. )
  19. // Note represents the contents of a git-note
  20. type Note []byte
  21. // Hash returns a hash of the given note
  22. func (n Note) Hash() string {
  23. return fmt.Sprintf("%x", sha1.Sum([]byte(n)))
  24. }
  25. // CommitDetails represents the contents of a commit.
  26. type CommitDetails struct {
  27. Author string `json:"author,omitempty"`
  28. AuthorEmail string `json:"authorEmail,omitempty"`
  29. AuthorTime string `json:"authorTime,omitempty"`
  30. Committer string `json:"committer,omitempty"`
  31. CommitterEmail string `json:"committerEmail,omitempty"`
  32. Tree string `json:"tree,omitempty"`
  33. Time string `json:"time,omitempty"`
  34. Parents []string `json:"parents,omitempty"`
  35. Summary string `json:"summary,omitempty"`
  36. }
  37. type TreeChild interface {
  38. // Type returns the type of the child object (e.g. "blob" vs. "tree").
  39. Type() string
  40. // Store writes the object to the repository and returns its hash.
  41. Store(repo Repo) (string, error)
  42. }
  43. // Blob represents a (non-directory) file stored in a repository.
  44. //
  45. // Blob objects are immutable.
  46. type Blob struct {
  47. savedHashes map[Repo]string
  48. contents string
  49. }
  50. // NewBlob returns a new *Blob object tied to the given repo with the given contents.
  51. func NewBlob(contents string) *Blob {
  52. savedHashes := make(map[Repo]string)
  53. return &Blob{
  54. savedHashes: savedHashes,
  55. contents: contents,
  56. }
  57. }
  58. func (b *Blob) Type() string {
  59. return "blob"
  60. }
  61. func (b *Blob) Store(repo Repo) (string, error) {
  62. if savedHash := b.savedHashes[repo]; savedHash != "" {
  63. return savedHash, nil
  64. }
  65. savedHash, err := repo.StoreBlob(b.Contents())
  66. if err == nil && savedHash != "" {
  67. b.savedHashes[repo] = savedHash
  68. }
  69. return savedHash, nil
  70. }
  71. // Contents returns the contents of the blob
  72. func (b *Blob) Contents() string {
  73. return b.contents
  74. }
  75. // Tree represents a directory stored in a repository.
  76. //
  77. // Tree objects are immutable.
  78. type Tree struct {
  79. savedHashes map[Repo]string
  80. contents map[string]TreeChild
  81. }
  82. // NewTree constructs a new *Tree object tied to the given repo with the given contents.
  83. func NewTree(contents map[string]TreeChild) *Tree {
  84. immutableContents := make(map[string]TreeChild)
  85. for k, v := range contents {
  86. immutableContents[k] = v
  87. }
  88. savedHashes := make(map[Repo]string)
  89. return &Tree{
  90. savedHashes: savedHashes,
  91. contents: immutableContents,
  92. }
  93. }
  94. func (t *Tree) Type() string {
  95. return "tree"
  96. }
  97. func (t *Tree) Store(repo Repo) (string, error) {
  98. if savedHash := t.savedHashes[repo]; savedHash != "" {
  99. return savedHash, nil
  100. }
  101. savedHash, err := repo.StoreTree(t.Contents())
  102. if err == nil && savedHash != "" {
  103. t.savedHashes[repo] = savedHash
  104. }
  105. return savedHash, nil
  106. }
  107. // Contents returns a map of the child elements of the tree.
  108. //
  109. // The returned map is mutable, but changes made to it have no
  110. // effect on the underly Tree object.
  111. func (t *Tree) Contents() map[string]TreeChild {
  112. result := make(map[string]TreeChild)
  113. for k, v := range t.contents {
  114. result[k] = v
  115. }
  116. return result
  117. }
  118. // Repo represents a source code repository.
  119. type Repo interface {
  120. // GetPath returns the path to the repo.
  121. GetPath() string
  122. // GetRepoStateHash returns a hash which embodies the entire current state of a repository.
  123. GetRepoStateHash() (string, error)
  124. // GetUserEmail returns the email address that the user has used to configure git.
  125. GetUserEmail() (string, error)
  126. // GetUserSigningKey returns the key id the user has configured for
  127. // sigining git artifacts.
  128. GetUserSigningKey() (string, error)
  129. // GetCoreEditor returns the name of the editor that the user has used to configure git.
  130. GetCoreEditor() (string, error)
  131. // GetSubmitStrategy returns the way in which a review is submitted
  132. GetSubmitStrategy() (string, error)
  133. // HasUncommittedChanges returns true if there are local, uncommitted changes.
  134. HasUncommittedChanges() (bool, error)
  135. // HasRef checks whether the specified ref exists in the repo.
  136. HasRef(ref string) (bool, error)
  137. // HasObject returns whether or not the repo contains an object with the given hash.
  138. HasObject(hash string) (bool, error)
  139. // VerifyCommit verifies that the supplied hash points to a known commit.
  140. VerifyCommit(hash string) error
  141. // VerifyGitRef verifies that the supplied ref points to a known commit.
  142. VerifyGitRef(ref string) error
  143. // GetHeadRef returns the ref that is the current HEAD.
  144. GetHeadRef() (string, error)
  145. // GetCommitHash returns the hash of the commit pointed to by the given ref.
  146. GetCommitHash(ref string) (string, error)
  147. // ResolveRefCommit returns the commit pointed to by the given ref, which may be a remote ref.
  148. //
  149. // This differs from GetCommitHash which only works on exact matches, in that it will try to
  150. // intelligently handle the scenario of a ref not existing locally, but being known to exist
  151. // in a remote repo.
  152. //
  153. // This method should be used when a command may be performed by either the reviewer or the
  154. // reviewee, while GetCommitHash should be used when the encompassing command should only be
  155. // performed by the reviewee.
  156. ResolveRefCommit(ref string) (string, error)
  157. // GetCommitMessage returns the message stored in the commit pointed to by the given ref.
  158. GetCommitMessage(ref string) (string, error)
  159. // GetCommitTime returns the commit time of the commit pointed to by the given ref.
  160. GetCommitTime(ref string) (string, error)
  161. // GetLastParent returns the last parent of the given commit (as ordered by git).
  162. GetLastParent(ref string) (string, error)
  163. // GetCommitDetails returns the details of a commit's metadata.
  164. GetCommitDetails(ref string) (*CommitDetails, error)
  165. // MergeBase determines if the first commit that is an ancestor of the two arguments.
  166. MergeBase(a, b string) (string, error)
  167. // IsAncestor determines if the first argument points to a commit that is an ancestor of the second.
  168. IsAncestor(ancestor, descendant string) (bool, error)
  169. // Diff computes the diff between two given commits.
  170. Diff(left, right string, diffArgs ...string) (string, error)
  171. // Show returns the contents of the given file at the given commit.
  172. Show(commit, path string) (string, error)
  173. // SwitchToRef changes the currently-checked-out ref.
  174. SwitchToRef(ref string) error
  175. // ArchiveRef adds the current commit pointed to by the 'ref' argument
  176. // under the ref specified in the 'archive' argument.
  177. //
  178. // Both the 'ref' and 'archive' arguments are expected to be the fully
  179. // qualified names of git refs (e.g. 'refs/heads/my-change' or
  180. // 'refs/archive/devtools').
  181. //
  182. // If the ref pointed to by the 'archive' argument does not exist
  183. // yet, then it will be created.
  184. ArchiveRef(ref, archive string) error
  185. // MergeRef merges the given ref into the current one.
  186. //
  187. // The ref argument is the ref to merge, and fastForward indicates that the
  188. // current ref should only move forward, as opposed to creating a bubble merge.
  189. // The messages argument(s) provide text that should be included in the default
  190. // merge commit message (separated by blank lines).
  191. MergeRef(ref string, fastForward bool, messages ...string) error
  192. // MergeAndSignRef merges the given ref into the current one and signs the
  193. // merge.
  194. //
  195. // The ref argument is the ref to merge, and fastForward indicates that the
  196. // current ref should only move forward, as opposed to creating a bubble merge.
  197. // The messages argument(s) provide text that should be included in the default
  198. // merge commit message (separated by blank lines).
  199. MergeAndSignRef(ref string, fastForward bool, messages ...string) error
  200. // RebaseRef rebases the current ref onto the given one.
  201. RebaseRef(ref string) error
  202. // RebaseAndSignRef rebases the current ref onto the given one and signs
  203. // the result.
  204. RebaseAndSignRef(ref string) error
  205. // ListCommits returns the list of commits reachable from the given ref.
  206. //
  207. // The generated list is in chronological order (with the oldest commit first).
  208. //
  209. // If the specified ref does not exist, then this method returns an empty result.
  210. ListCommits(ref string) []string
  211. // ListCommitsBetween returns the list of commits between the two given revisions.
  212. //
  213. // The "from" parameter is the starting point (exclusive), and the "to"
  214. // parameter is the ending point (inclusive).
  215. //
  216. // The "from" commit does not need to be an ancestor of the "to" commit. If it
  217. // is not, then the merge base of the two is used as the starting point.
  218. // Admittedly, this makes calling these the "between" commits is a bit of a
  219. // misnomer, but it also makes the method easier to use when you want to
  220. // generate the list of changes in a feature branch, as it eliminates the need
  221. // to explicitly calculate the merge base. This also makes the semantics of the
  222. // method compatible with git's built-in "rev-list" command.
  223. //
  224. // The generated list is in chronological order (with the oldest commit first).
  225. ListCommitsBetween(from, to string) ([]string, error)
  226. // StoreBlob writes the given file contents to the repository and returns its hash.
  227. StoreBlob(contents string) (string, error)
  228. // StoreTree writes the given file tree contents to the repository and returns its hash.
  229. StoreTree(contents map[string]TreeChild) (string, error)
  230. // ReadTree reads the file tree pointed to by the given ref or hash from the repository.
  231. ReadTree(ref string) (*Tree, error)
  232. // CreateCommit creates a commit object and returns its hash.
  233. CreateCommit(details *CommitDetails) (string, error)
  234. // CreateCommitWithTree creates a commit object with the given tree and returns its hash.
  235. CreateCommitWithTree(details *CommitDetails, t *Tree) (string, error)
  236. // SetRef sets the commit pointed to by the specified ref to `newCommitHash`,
  237. // iff the ref currently points `previousCommitHash`.
  238. SetRef(ref, newCommitHash, previousCommitHash string) error
  239. // GetNotes reads the notes from the given ref that annotate the given revision.
  240. GetNotes(notesRef, revision string) []Note
  241. // GetAllNotes reads the contents of the notes under the given ref for every commit.
  242. //
  243. // The returned value is a mapping from commit hash to the list of notes for that commit.
  244. //
  245. // This is the batch version of the corresponding GetNotes(...) method.
  246. GetAllNotes(notesRef string) (map[string][]Note, error)
  247. // AppendNote appends a note to a revision under the given ref.
  248. AppendNote(ref, revision string, note Note) error
  249. // ListNotedRevisions returns the collection of revisions that are annotated by notes in the given ref.
  250. ListNotedRevisions(notesRef string) []string
  251. // Remotes returns a list of the remotes.
  252. Remotes() ([]string, error)
  253. // Fetch fetches from the given remote using the supplied refspecs.
  254. Fetch(remote string, refspecs ...string) error
  255. // PushNotes pushes git notes to a remote repo.
  256. PushNotes(remote, notesRefPattern string) error
  257. // PullNotes fetches the contents of the given notes ref from a remote repo,
  258. // and then merges them with the corresponding local notes using the
  259. // "cat_sort_uniq" strategy.
  260. PullNotes(remote, notesRefPattern string) error
  261. // PushNotesAndArchive pushes the given notes and archive refs to a remote repo.
  262. PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error
  263. // PullNotesAndArchive fetches the contents of the notes and archives refs from
  264. // a remote repo, and merges them with the corresponding local refs.
  265. //
  266. // For notes refs, we assume that every note can be automatically merged using
  267. // the 'cat_sort_uniq' strategy (the git-appraise schemas fit that requirement),
  268. // so we automatically merge the remote notes into the local notes.
  269. //
  270. // For "archive" refs, they are expected to be used solely for maintaining
  271. // reachability of commits that are part of the history of any reviews,
  272. // so we do not maintain any consistency with their tree objects. Instead,
  273. // we merely ensure that their history graph includes every commit that we
  274. // intend to keep.
  275. PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error
  276. // MergeNotes merges in the remote's state of the archives reference into
  277. // the local repository's.
  278. MergeNotes(remote, notesRefPattern string) error
  279. // MergeArchives merges in the remote's state of the archives reference
  280. // into the local repository's.
  281. MergeArchives(remote, archiveRefPattern string) error
  282. // FetchAndReturnNewReviewHashes fetches the notes "branches" and then
  283. // susses out the IDs (the revision the review points to) of any new
  284. // reviews, then returns that list of IDs.
  285. //
  286. // This is accomplished by determining which files in the notes tree have
  287. // changed because the _names_ of these files correspond to the revisions
  288. // they point to.
  289. FetchAndReturnNewReviewHashes(remote, notesRefPattern string, devtoolsRefPatterns ...string) ([]string, error)
  290. // Push pushes the given refs to a remote repo.
  291. Push(remote string, refPattern ...string) error
  292. }