diff options
Diffstat (limited to 'third_party/go/git-appraise/repository')
-rw-r--r-- | third_party/go/git-appraise/repository/git.go | 987 | ||||
-rw-r--r-- | third_party/go/git-appraise/repository/git_test.go | 94 | ||||
-rw-r--r-- | third_party/go/git-appraise/repository/mock_repo.go | 613 | ||||
-rw-r--r-- | third_party/go/git-appraise/repository/repo.go | 221 |
4 files changed, 0 insertions, 1915 deletions
diff --git a/third_party/go/git-appraise/repository/git.go b/third_party/go/git-appraise/repository/git.go deleted file mode 100644 index 31d27ea6d2ec..000000000000 --- a/third_party/go/git-appraise/repository/git.go +++ /dev/null @@ -1,987 +0,0 @@ -/* -Copyright 2015 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package repository contains helper methods for working with the Git repo. -package repository - -import ( - "bufio" - "bytes" - "crypto/sha1" - "encoding/json" - "fmt" - "io" - "os" - "os/exec" - "strconv" - "strings" -) - -const branchRefPrefix = "refs/heads/" - -// GitRepo represents an instance of a (local) git repository. -type GitRepo struct { - Path string -} - -// Run the given git command with the given I/O reader/writers, returning an error if it fails. -func (repo *GitRepo) runGitCommandWithIO(stdin io.Reader, stdout, stderr io.Writer, args ...string) error { - cmd := exec.Command("git", args...) - cmd.Dir = repo.Path - cmd.Stdin = stdin - cmd.Stdout = stdout - cmd.Stderr = stderr - return cmd.Run() -} - -// Run the given git command and return its stdout, or an error if the command fails. -func (repo *GitRepo) runGitCommandRaw(args ...string) (string, string, error) { - var stdout bytes.Buffer - var stderr bytes.Buffer - err := repo.runGitCommandWithIO(nil, &stdout, &stderr, args...) - return strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err -} - -// Run the given git command and return its stdout, or an error if the command fails. -func (repo *GitRepo) runGitCommand(args ...string) (string, error) { - stdout, stderr, err := repo.runGitCommandRaw(args...) - if err != nil { - if stderr == "" { - stderr = "Error running git command: " + strings.Join(args, " ") - } - err = fmt.Errorf(stderr) - } - return stdout, err -} - -// Run the given git command using the same stdin, stdout, and stderr as the review tool. -func (repo *GitRepo) runGitCommandInline(args ...string) error { - return repo.runGitCommandWithIO(os.Stdin, os.Stdout, os.Stderr, args...) -} - -// NewGitRepo determines if the given working directory is inside of a git repository, -// and returns the corresponding GitRepo instance if it is. -func NewGitRepo(path string) (*GitRepo, error) { - repo := &GitRepo{Path: path} - _, _, err := repo.runGitCommandRaw("rev-parse") - if err == nil { - return repo, nil - } - if _, ok := err.(*exec.ExitError); ok { - return nil, err - } - return nil, err -} - -// GetPath returns the path to the repo. -func (repo *GitRepo) GetPath() string { - return repo.Path -} - -// GetRepoStateHash returns a hash which embodies the entire current state of a repository. -func (repo *GitRepo) GetRepoStateHash() (string, error) { - stateSummary, error := repo.runGitCommand("show-ref") - return fmt.Sprintf("%x", sha1.Sum([]byte(stateSummary))), error -} - -// GetUserEmail returns the email address that the user has used to configure git. -func (repo *GitRepo) GetUserEmail() (string, error) { - return repo.runGitCommand("config", "user.email") -} - -// GetUserSigningKey returns the key id the user has configured for -// sigining git artifacts. -func (repo *GitRepo) GetUserSigningKey() (string, error) { - return repo.runGitCommand("config", "user.signingKey") -} - -// GetCoreEditor returns the name of the editor that the user has used to configure git. -func (repo *GitRepo) GetCoreEditor() (string, error) { - return repo.runGitCommand("var", "GIT_EDITOR") -} - -// GetSubmitStrategy returns the way in which a review is submitted -func (repo *GitRepo) GetSubmitStrategy() (string, error) { - submitStrategy, _ := repo.runGitCommand("config", "appraise.submit") - return submitStrategy, nil -} - -// HasUncommittedChanges returns true if there are local, uncommitted changes. -func (repo *GitRepo) HasUncommittedChanges() (bool, error) { - out, err := repo.runGitCommand("status", "--porcelain") - if err != nil { - return false, err - } - if len(out) > 0 { - return true, nil - } - return false, nil -} - -// VerifyCommit verifies that the supplied hash points to a known commit. -func (repo *GitRepo) VerifyCommit(hash string) error { - out, err := repo.runGitCommand("cat-file", "-t", hash) - if err != nil { - return err - } - objectType := strings.TrimSpace(string(out)) - if objectType != "commit" { - return fmt.Errorf("Hash %q points to a non-commit object of type %q", hash, objectType) - } - return nil -} - -// VerifyGitRef verifies that the supplied ref points to a known commit. -func (repo *GitRepo) VerifyGitRef(ref string) error { - _, err := repo.runGitCommand("show-ref", "--verify", ref) - return err -} - -// GetHeadRef returns the ref that is the current HEAD. -func (repo *GitRepo) GetHeadRef() (string, error) { - return repo.runGitCommand("symbolic-ref", "HEAD") -} - -// GetCommitHash returns the hash of the commit pointed to by the given ref. -func (repo *GitRepo) GetCommitHash(ref string) (string, error) { - return repo.runGitCommand("show", "-s", "--format=%H", ref) -} - -// ResolveRefCommit returns the commit pointed to by the given ref, which may be a remote ref. -// -// This differs from GetCommitHash which only works on exact matches, in that it will try to -// intelligently handle the scenario of a ref not existing locally, but being known to exist -// in a remote repo. -// -// This method should be used when a command may be performed by either the reviewer or the -// reviewee, while GetCommitHash should be used when the encompassing command should only be -// performed by the reviewee. -func (repo *GitRepo) ResolveRefCommit(ref string) (string, error) { - if err := repo.VerifyGitRef(ref); err == nil { - return repo.GetCommitHash(ref) - } - if strings.HasPrefix(ref, "refs/heads/") { - // The ref is a branch. Check if it exists in exactly one remote - pattern := strings.Replace(ref, "refs/heads", "**", 1) - matchingOutput, err := repo.runGitCommand("for-each-ref", "--format=%(refname)", pattern) - if err != nil { - return "", err - } - matchingRefs := strings.Split(matchingOutput, "\n") - if len(matchingRefs) == 1 && matchingRefs[0] != "" { - // There is exactly one match - return repo.GetCommitHash(matchingRefs[0]) - } - return "", fmt.Errorf("Unable to find a git ref matching the pattern %q", pattern) - } - return "", fmt.Errorf("Unknown git ref %q", ref) -} - -// GetCommitMessage returns the message stored in the commit pointed to by the given ref. -func (repo *GitRepo) GetCommitMessage(ref string) (string, error) { - return repo.runGitCommand("show", "-s", "--format=%B", ref) -} - -// GetCommitTime returns the commit time of the commit pointed to by the given ref. -func (repo *GitRepo) GetCommitTime(ref string) (string, error) { - return repo.runGitCommand("show", "-s", "--format=%ct", ref) -} - -// GetLastParent returns the last parent of the given commit (as ordered by git). -func (repo *GitRepo) GetLastParent(ref string) (string, error) { - return repo.runGitCommand("rev-list", "--skip", "1", "-n", "1", ref) -} - -// GetCommitDetails returns the details of a commit's metadata. -func (repo GitRepo) GetCommitDetails(ref string) (*CommitDetails, error) { - var err error - show := func(formatString string) (result string) { - if err != nil { - return "" - } - result, err = repo.runGitCommand("show", "-s", ref, fmt.Sprintf("--format=tformat:%s", formatString)) - return result - } - - jsonFormatString := "{\"tree\":\"%T\", \"time\": \"%at\"}" - detailsJSON := show(jsonFormatString) - if err != nil { - return nil, err - } - var details CommitDetails - err = json.Unmarshal([]byte(detailsJSON), &details) - if err != nil { - return nil, err - } - details.Author = show("%an") - details.AuthorEmail = show("%ae") - details.Summary = show("%s") - parentsString := show("%P") - details.Parents = strings.Split(parentsString, " ") - if err != nil { - return nil, err - } - return &details, nil -} - -// MergeBase determines if the first commit that is an ancestor of the two arguments. -func (repo *GitRepo) MergeBase(a, b string) (string, error) { - return repo.runGitCommand("merge-base", a, b) -} - -// IsAncestor determines if the first argument points to a commit that is an ancestor of the second. -func (repo *GitRepo) IsAncestor(ancestor, descendant string) (bool, error) { - _, _, err := repo.runGitCommandRaw("merge-base", "--is-ancestor", ancestor, descendant) - if err == nil { - return true, nil - } - if _, ok := err.(*exec.ExitError); ok { - return false, nil - } - return false, fmt.Errorf("Error while trying to determine commit ancestry: %v", err) -} - -// Diff computes the diff between two given commits. -func (repo *GitRepo) Diff(left, right string, diffArgs ...string) (string, error) { - args := []string{"diff"} - args = append(args, diffArgs...) - args = append(args, fmt.Sprintf("%s..%s", left, right)) - return repo.runGitCommand(args...) -} - -// Show returns the contents of the given file at the given commit. -func (repo *GitRepo) Show(commit, path string) (string, error) { - return repo.runGitCommand("show", fmt.Sprintf("%s:%s", commit, path)) -} - -// SwitchToRef changes the currently-checked-out ref. -func (repo *GitRepo) SwitchToRef(ref string) error { - // If the ref starts with "refs/heads/", then we have to trim that prefix, - // or else we will wind up in a detached HEAD state. - if strings.HasPrefix(ref, branchRefPrefix) { - ref = ref[len(branchRefPrefix):] - } - _, err := repo.runGitCommand("checkout", ref) - return err -} - -// mergeArchives merges two archive refs. -func (repo *GitRepo) mergeArchives(archive, remoteArchive string) error { - remoteHash, err := repo.GetCommitHash(remoteArchive) - if err != nil { - return err - } - if remoteHash == "" { - // The remote archive does not exist, so we have nothing to do - return nil - } - - archiveHash, err := repo.GetCommitHash(archive) - if err != nil { - return err - } - if archiveHash == "" { - // The local archive does not exist, so we merely need to set it - _, err := repo.runGitCommand("update-ref", archive, remoteHash) - return err - } - - isAncestor, err := repo.IsAncestor(archiveHash, remoteHash) - if err != nil { - return err - } - if isAncestor { - // The archive can simply be fast-forwarded - _, err := repo.runGitCommand("update-ref", archive, remoteHash, archiveHash) - return err - } - - // Create a merge commit of the two archives - refDetails, err := repo.GetCommitDetails(remoteArchive) - if err != nil { - return err - } - newArchiveHash, err := repo.runGitCommand("commit-tree", "-p", remoteHash, "-p", archiveHash, "-m", "Merge local and remote archives", refDetails.Tree) - if err != nil { - return err - } - newArchiveHash = strings.TrimSpace(newArchiveHash) - _, err = repo.runGitCommand("update-ref", archive, newArchiveHash, archiveHash) - return err -} - -// ArchiveRef adds the current commit pointed to by the 'ref' argument -// under the ref specified in the 'archive' argument. -// -// Both the 'ref' and 'archive' arguments are expected to be the fully -// qualified names of git refs (e.g. 'refs/heads/my-change' or -// 'refs/devtools/archives/reviews'). -// -// If the ref pointed to by the 'archive' argument does not exist -// yet, then it will be created. -func (repo *GitRepo) ArchiveRef(ref, archive string) error { - refHash, err := repo.GetCommitHash(ref) - if err != nil { - return err - } - refDetails, err := repo.GetCommitDetails(ref) - if err != nil { - return err - } - - commitTreeArgs := []string{"commit-tree"} - archiveHash, err := repo.GetCommitHash(archive) - if err != nil { - archiveHash = "" - } else { - commitTreeArgs = append(commitTreeArgs, "-p", archiveHash) - } - commitTreeArgs = append(commitTreeArgs, "-p", refHash, "-m", fmt.Sprintf("Archive %s", refHash), refDetails.Tree) - newArchiveHash, err := repo.runGitCommand(commitTreeArgs...) - if err != nil { - return err - } - newArchiveHash = strings.TrimSpace(newArchiveHash) - updateRefArgs := []string{"update-ref", archive, newArchiveHash} - if archiveHash != "" { - updateRefArgs = append(updateRefArgs, archiveHash) - } - _, err = repo.runGitCommand(updateRefArgs...) - return err -} - -// MergeRef merges the given ref into the current one. -// -// The ref argument is the ref to merge, and fastForward indicates that the -// current ref should only move forward, as opposed to creating a bubble merge. -// The messages argument(s) provide text that should be included in the default -// merge commit message (separated by blank lines). -func (repo *GitRepo) MergeRef(ref string, fastForward bool, messages ...string) error { - args := []string{"merge"} - if fastForward { - args = append(args, "--ff", "--ff-only") - } else { - args = append(args, "--no-ff") - } - if len(messages) > 0 { - commitMessage := strings.Join(messages, "\n\n") - args = append(args, "-e", "-m", commitMessage) - } - args = append(args, ref) - return repo.runGitCommandInline(args...) -} - -// MergeAndSignRef merges the given ref into the current one and signs the -// merge. -// -// The ref argument is the ref to merge, and fastForward indicates that the -// current ref should only move forward, as opposed to creating a bubble merge. -// The messages argument(s) provide text that should be included in the default -// merge commit message (separated by blank lines). -func (repo *GitRepo) MergeAndSignRef(ref string, fastForward bool, - messages ...string) error { - - args := []string{"merge"} - if fastForward { - args = append(args, "--ff", "--ff-only", "-S") - } else { - args = append(args, "--no-ff", "-S") - } - if len(messages) > 0 { - commitMessage := strings.Join(messages, "\n\n") - args = append(args, "-e", "-m", commitMessage) - } - args = append(args, ref) - return repo.runGitCommandInline(args...) -} - -// RebaseRef rebases the current ref onto the given one. -func (repo *GitRepo) RebaseRef(ref string) error { - return repo.runGitCommandInline("rebase", "-i", ref) -} - -// RebaseAndSignRef rebases the current ref onto the given one and signs the -// result. -func (repo *GitRepo) RebaseAndSignRef(ref string) error { - return repo.runGitCommandInline("rebase", "-S", "-i", ref) -} - -// ListCommits returns the list of commits reachable from the given ref. -// -// The generated list is in chronological order (with the oldest commit first). -// -// If the specified ref does not exist, then this method returns an empty result. -func (repo *GitRepo) ListCommits(ref string) []string { - var stdout bytes.Buffer - var stderr bytes.Buffer - if err := repo.runGitCommandWithIO(nil, &stdout, &stderr, "rev-list", "--reverse", ref); err != nil { - return nil - } - - byteLines := bytes.Split(stdout.Bytes(), []byte("\n")) - var commits []string - for _, byteLine := range byteLines { - commits = append(commits, string(byteLine)) - } - return commits -} - -// ListCommitsBetween returns the list of commits between the two given revisions. -// -// The "from" parameter is the starting point (exclusive), and the "to" -// parameter is the ending point (inclusive). -// -// The "from" commit does not need to be an ancestor of the "to" commit. If it -// is not, then the merge base of the two is used as the starting point. -// Admittedly, this makes calling these the "between" commits is a bit of a -// misnomer, but it also makes the method easier to use when you want to -// generate the list of changes in a feature branch, as it eliminates the need -// to explicitly calculate the merge base. This also makes the semantics of the -// method compatible with git's built-in "rev-list" command. -// -// The generated list is in chronological order (with the oldest commit first). -func (repo *GitRepo) ListCommitsBetween(from, to string) ([]string, error) { - out, err := repo.runGitCommand("rev-list", "--reverse", from+".."+to) - if err != nil { - return nil, err - } - if out == "" { - return nil, nil - } - return strings.Split(out, "\n"), nil -} - -// GetNotes uses the "git" command-line tool to read the notes from the given ref for a given revision. -func (repo *GitRepo) GetNotes(notesRef, revision string) []Note { - var notes []Note - rawNotes, err := repo.runGitCommand("notes", "--ref", notesRef, "show", revision) - if err != nil { - // We just assume that this means there are no notes - return nil - } - for _, line := range strings.Split(rawNotes, "\n") { - notes = append(notes, Note([]byte(line))) - } - return notes -} - -func stringsReader(s []*string) io.Reader { - var subReaders []io.Reader - for _, strPtr := range s { - subReader := strings.NewReader(*strPtr) - subReaders = append(subReaders, subReader, strings.NewReader("\n")) - } - return io.MultiReader(subReaders...) -} - -// splitBatchCheckOutput parses the output of a 'git cat-file --batch-check=...' command. -// -// The output is expected to be formatted as a series of entries, with each -// entry consisting of: -// 1. The SHA1 hash of the git object being output, followed by a space. -// 2. The git "type" of the object (commit, blob, tree, missing, etc), followed by a newline. -// -// To generate this format, make sure that the 'git cat-file' command includes -// the argument '--batch-check=%(objectname) %(objecttype)'. -// -// The return value is a map from object hash to a boolean indicating if that object is a commit. -func splitBatchCheckOutput(out *bytes.Buffer) (map[string]bool, error) { - isCommit := make(map[string]bool) - reader := bufio.NewReader(out) - for { - nameLine, err := reader.ReadString(byte(' ')) - if err == io.EOF { - return isCommit, nil - } - if err != nil { - return nil, fmt.Errorf("Failure while reading the next object name: %v", err) - } - nameLine = strings.TrimSuffix(nameLine, " ") - typeLine, err := reader.ReadString(byte('\n')) - if err != nil && err != io.EOF { - return nil, fmt.Errorf("Failure while reading the next object type: %q - %v", nameLine, err) - } - typeLine = strings.TrimSuffix(typeLine, "\n") - if typeLine == "commit" { - isCommit[nameLine] = true - } - } -} - -// splitBatchCatFileOutput parses the output of a 'git cat-file --batch=...' command. -// -// The output is expected to be formatted as a series of entries, with each -// entry consisting of: -// 1. The SHA1 hash of the git object being output, followed by a newline. -// 2. The size of the object's contents in bytes, followed by a newline. -// 3. The objects contents. -// -// To generate this format, make sure that the 'git cat-file' command includes -// the argument '--batch=%(objectname)\n%(objectsize)'. -func splitBatchCatFileOutput(out *bytes.Buffer) (map[string][]byte, error) { - contentsMap := make(map[string][]byte) - reader := bufio.NewReader(out) - for { - nameLine, err := reader.ReadString(byte('\n')) - if strings.HasSuffix(nameLine, "\n") { - nameLine = strings.TrimSuffix(nameLine, "\n") - } - if err == io.EOF { - return contentsMap, nil - } - if err != nil { - return nil, fmt.Errorf("Failure while reading the next object name: %v", err) - } - sizeLine, err := reader.ReadString(byte('\n')) - if strings.HasSuffix(sizeLine, "\n") { - sizeLine = strings.TrimSuffix(sizeLine, "\n") - } - if err != nil { - return nil, fmt.Errorf("Failure while reading the next object size: %q - %v", nameLine, err) - } - size, err := strconv.Atoi(sizeLine) - if err != nil { - return nil, fmt.Errorf("Failure while parsing the next object size: %q - %v", nameLine, err) - } - contentBytes := make([]byte, size, size) - readDest := contentBytes - len := 0 - err = nil - for err == nil && len < size { - nextLen := 0 - nextLen, err = reader.Read(readDest) - len += nextLen - readDest = contentBytes[len:] - } - contentsMap[nameLine] = contentBytes - if err == io.EOF { - return contentsMap, nil - } - if err != nil { - return nil, err - } - for bs, err := reader.Peek(1); err == nil && bs[0] == byte('\n'); bs, err = reader.Peek(1) { - reader.ReadByte() - } - } -} - -// notesMapping represents the association between a git object and the notes for that object. -type notesMapping struct { - ObjectHash *string - NotesHash *string -} - -// notesOverview represents a high-level overview of all the notes under a single notes ref. -type notesOverview struct { - NotesMappings []*notesMapping - ObjectHashesReader io.Reader - NotesHashesReader io.Reader -} - -// notesOverview returns an overview of the git notes stored under the given ref. -func (repo *GitRepo) notesOverview(notesRef string) (*notesOverview, error) { - var stdout bytes.Buffer - var stderr bytes.Buffer - if err := repo.runGitCommandWithIO(nil, &stdout, &stderr, "notes", "--ref", notesRef, "list"); err != nil { - return nil, err - } - - var notesMappings []*notesMapping - var objHashes []*string - var notesHashes []*string - outScanner := bufio.NewScanner(&stdout) - for outScanner.Scan() { - line := outScanner.Text() - lineParts := strings.Split(line, " ") - if len(lineParts) != 2 { - return nil, fmt.Errorf("Malformed output line from 'git-notes list': %q", line) - } - objHash := &lineParts[1] - notesHash := &lineParts[0] - notesMappings = append(notesMappings, ¬esMapping{ - ObjectHash: objHash, - NotesHash: notesHash, - }) - objHashes = append(objHashes, objHash) - notesHashes = append(notesHashes, notesHash) - } - err := outScanner.Err() - if err != nil && err != io.EOF { - return nil, fmt.Errorf("Failure parsing the output of 'git-notes list': %v", err) - } - return ¬esOverview{ - NotesMappings: notesMappings, - ObjectHashesReader: stringsReader(objHashes), - NotesHashesReader: stringsReader(notesHashes), - }, nil -} - -// getIsCommitMap returns a mapping of all the annotated objects that are commits. -func (overview *notesOverview) getIsCommitMap(repo *GitRepo) (map[string]bool, error) { - var stdout bytes.Buffer - var stderr bytes.Buffer - if err := repo.runGitCommandWithIO(overview.ObjectHashesReader, &stdout, &stderr, "cat-file", "--batch-check=%(objectname) %(objecttype)"); err != nil { - return nil, fmt.Errorf("Failure performing a batch file check: %v", err) - } - isCommit, err := splitBatchCheckOutput(&stdout) - if err != nil { - return nil, fmt.Errorf("Failure parsing the output of a batch file check: %v", err) - } - return isCommit, nil -} - -// getNoteContentsMap returns a mapping from all the notes hashes to their contents. -func (overview *notesOverview) getNoteContentsMap(repo *GitRepo) (map[string][]byte, error) { - var stdout bytes.Buffer - var stderr bytes.Buffer - if err := repo.runGitCommandWithIO(overview.NotesHashesReader, &stdout, &stderr, "cat-file", "--batch=%(objectname)\n%(objectsize)"); err != nil { - return nil, fmt.Errorf("Failure performing a batch file read: %v", err) - } - noteContentsMap, err := splitBatchCatFileOutput(&stdout) - if err != nil { - return nil, fmt.Errorf("Failure parsing the output of a batch file read: %v", err) - } - return noteContentsMap, nil -} - -// GetAllNotes reads the contents of the notes under the given ref for every commit. -// -// The returned value is a mapping from commit hash to the list of notes for that commit. -// -// This is the batch version of the corresponding GetNotes(...) method. -func (repo *GitRepo) GetAllNotes(notesRef string) (map[string][]Note, error) { - // This code is unfortunately quite complicated, but it needs to be so. - // - // Conceptually, this is equivalent to: - // result := make(map[string][]Note) - // for _, commit := range repo.ListNotedRevisions(notesRef) { - // result[commit] = repo.GetNotes(notesRef, commit) - // } - // return result, nil - // - // However, that logic would require separate executions of the 'git' - // command for every annotated commit. For a repo with 10s of thousands - // of reviews, that would mean calling Cmd.Run(...) 10s of thousands of - // times. That, in turn, would take so long that the tool would be unusable. - // - // This method avoids that by taking advantage of the 'git cat-file --batch="..."' - // command. That allows us to use a single invocation of Cmd.Run(...) to - // inspect multiple git objects at once. - // - // As such, regardless of the number of reviews in a repo, we can get all - // of the notes using a total of three invocations of Cmd.Run(...): - // 1. One to list all the annotated objects (and their notes hash) - // 2. A second one to filter out all of the annotated objects that are not commits. - // 3. A final one to get the contents of all of the notes blobs. - overview, err := repo.notesOverview(notesRef) - if err != nil { - return nil, err - } - isCommit, err := overview.getIsCommitMap(repo) - if err != nil { - return nil, fmt.Errorf("Failure building the set of commit objects: %v", err) - } - noteContentsMap, err := overview.getNoteContentsMap(repo) - if err != nil { - return nil, fmt.Errorf("Failure building the mapping from notes hash to contents: %v", err) - } - commitNotesMap := make(map[string][]Note) - for _, notesMapping := range overview.NotesMappings { - if !isCommit[*notesMapping.ObjectHash] { - continue - } - noteBytes := noteContentsMap[*notesMapping.NotesHash] - byteSlices := bytes.Split(noteBytes, []byte("\n")) - var notes []Note - for _, slice := range byteSlices { - notes = append(notes, Note(slice)) - } - commitNotesMap[*notesMapping.ObjectHash] = notes - } - - return commitNotesMap, nil -} - -// AppendNote appends a note to a revision under the given ref. -func (repo *GitRepo) AppendNote(notesRef, revision string, note Note) error { - _, err := repo.runGitCommand("notes", "--ref", notesRef, "append", "-m", string(note), revision) - return err -} - -// ListNotedRevisions returns the collection of revisions that are annotated by notes in the given ref. -func (repo *GitRepo) ListNotedRevisions(notesRef string) []string { - var revisions []string - notesListOut, err := repo.runGitCommand("notes", "--ref", notesRef, "list") - if err != nil { - return nil - } - notesList := strings.Split(notesListOut, "\n") - for _, notePair := range notesList { - noteParts := strings.SplitN(notePair, " ", 2) - if len(noteParts) == 2 { - objHash := noteParts[1] - objType, err := repo.runGitCommand("cat-file", "-t", objHash) - // If a note points to an object that we do not know about (yet), then err will not - // be nil. We can safely just ignore those notes. - if err == nil && objType == "commit" { - revisions = append(revisions, objHash) - } - } - } - return revisions -} - -// PushNotes pushes git notes to a remote repo. -func (repo *GitRepo) PushNotes(remote, notesRefPattern string) error { - refspec := fmt.Sprintf("%s:%s", notesRefPattern, notesRefPattern) - - // The push is liable to fail if the user forgot to do a pull first, so - // we treat errors as user errors rather than fatal errors. - err := repo.runGitCommandInline("push", remote, refspec) - if err != nil { - return fmt.Errorf("Failed to push to the remote '%s': %v", remote, err) - } - return nil -} - -// PushNotesAndArchive pushes the given notes and archive refs to a remote repo. -func (repo *GitRepo) PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error { - notesRefspec := fmt.Sprintf("%s:%s", notesRefPattern, notesRefPattern) - archiveRefspec := fmt.Sprintf("%s:%s", archiveRefPattern, archiveRefPattern) - err := repo.runGitCommandInline("push", remote, notesRefspec, archiveRefspec) - if err != nil { - return fmt.Errorf("Failed to push the local archive to the remote '%s': %v", remote, err) - } - return nil -} - -func getRemoteNotesRef(remote, localNotesRef string) string { - relativeNotesRef := strings.TrimPrefix(localNotesRef, "refs/notes/") - return "refs/notes/" + remote + "/" + relativeNotesRef -} - -// MergeNotes merges in the remote's state of the notes reference into the -// local repository's. -func (repo *GitRepo) MergeNotes(remote, notesRefPattern string) error { - remoteRefs, err := repo.runGitCommand("ls-remote", remote, notesRefPattern) - if err != nil { - return err - } - for _, line := range strings.Split(remoteRefs, "\n") { - lineParts := strings.Split(line, "\t") - if len(lineParts) == 2 { - ref := lineParts[1] - remoteRef := getRemoteNotesRef(remote, ref) - _, err := repo.runGitCommand("notes", "--ref", ref, "merge", remoteRef, "-s", "cat_sort_uniq") - if err != nil { - return err - } - } - } - return nil -} - -// PullNotes fetches the contents of the given notes ref from a remote repo, -// and then merges them with the corresponding local notes using the -// "cat_sort_uniq" strategy. -func (repo *GitRepo) PullNotes(remote, notesRefPattern string) error { - remoteNotesRefPattern := getRemoteNotesRef(remote, notesRefPattern) - fetchRefSpec := fmt.Sprintf("+%s:%s", notesRefPattern, remoteNotesRefPattern) - err := repo.runGitCommandInline("fetch", remote, fetchRefSpec) - if err != nil { - return err - } - - return repo.MergeNotes(remote, notesRefPattern) -} - -func getRemoteArchiveRef(remote, archiveRefPattern string) string { - relativeArchiveRef := strings.TrimPrefix(archiveRefPattern, "refs/devtools/archives/") - return "refs/devtools/remoteArchives/" + remote + "/" + relativeArchiveRef -} - -// MergeArchives merges in the remote's state of the archives reference into -// the local repository's. -func (repo *GitRepo) MergeArchives(remote, archiveRefPattern string) error { - remoteRefs, err := repo.runGitCommand("ls-remote", remote, archiveRefPattern) - if err != nil { - return err - } - for _, line := range strings.Split(remoteRefs, "\n") { - lineParts := strings.Split(line, "\t") - if len(lineParts) == 2 { - ref := lineParts[1] - remoteRef := getRemoteArchiveRef(remote, ref) - if err := repo.mergeArchives(ref, remoteRef); err != nil { - return err - } - } - } - return nil -} - -func (repo *GitRepo) fetchNotes(remote, notesRefPattern, - archiveRefPattern string) error { - - remoteArchiveRef := getRemoteArchiveRef(remote, archiveRefPattern) - archiveFetchRefSpec := fmt.Sprintf("+%s:%s", archiveRefPattern, remoteArchiveRef) - - remoteNotesRefPattern := getRemoteNotesRef(remote, notesRefPattern) - notesFetchRefSpec := fmt.Sprintf("+%s:%s", notesRefPattern, remoteNotesRefPattern) - - return repo.runGitCommandInline("fetch", remote, notesFetchRefSpec, archiveFetchRefSpec) -} - -// PullNotesAndArchive fetches the contents of the notes and archives refs from -// a remote repo, and merges them with the corresponding local refs. -// -// For notes refs, we assume that every note can be automatically merged using -// the 'cat_sort_uniq' strategy (the git-appraise schemas fit that requirement), -// so we automatically merge the remote notes into the local notes. -// -// For "archive" refs, they are expected to be used solely for maintaining -// reachability of commits that are part of the history of any reviews, -// so we do not maintain any consistency with their tree objects. Instead, -// we merely ensure that their history graph includes every commit that we -// intend to keep. -func (repo *GitRepo) PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error { - err := repo.fetchNotes(remote, notesRefPattern, archiveRefPattern) - if err != nil { - return err - } - - err = repo.MergeNotes(remote, notesRefPattern) - if err != nil { - return err - } - return repo.MergeArchives(remote, archiveRefPattern) -} - -// FetchAndReturnNewReviewHashes fetches the notes "branches" and then susses -// out the IDs (the revision the review points to) of any new reviews, then -// returns that list of IDs. -// -// This is accomplished by determining which files in the notes tree have -// changed because the _names_ of these files correspond to the revisions they -// point to. -func (repo *GitRepo) FetchAndReturnNewReviewHashes(remote, notesRefPattern, - archiveRefPattern string) ([]string, error) { - - // Record the current state of the reviews and comments refs. - var ( - getAllRevs, getAllComs bool - reviewsList, commentsList []string - ) - reviewBeforeHash, err := repo.GetCommitHash( - "notes/" + remote + "/devtools/reviews") - getAllRevs = err != nil - - commentBeforeHash, err := repo.GetCommitHash( - "notes/" + remote + "/devtools/discuss") - getAllComs = err != nil - - // Update them from the remote. - err = repo.fetchNotes(remote, notesRefPattern, archiveRefPattern) - if err != nil { - return nil, err - } - - // Now, if either of these are new refs, we just use the whole tree at that - // new ref. Otherwise we see which reviews or comments changed and collect - // them into a list. - if getAllRevs { - hash, err := repo.GetCommitHash( - "notes/" + remote + "/devtools/reviews") - // It is possible that even after we've pulled that this ref still - // isn't present (because there are no reviews yet). - if err == nil { - rvws, err := repo.runGitCommand("ls-tree", "-r", "--name-only", - hash) - if err != nil { - return nil, err - } - reviewsList = strings.Split(strings.Replace(rvws, "/", "", -1), - "\n") - } - } else { - reviewAfterHash, err := repo.GetCommitHash( - "notes/" + remote + "/devtools/reviews") - if err != nil { - return nil, err - } - - // Only run through this if the fetch fetched new revisions. - // Otherwise leave reviewsList as its default value, an empty slice - // of strings. - if reviewBeforeHash != reviewAfterHash { - newReviewsRaw, err := repo.runGitCommand("diff", "--name-only", - reviewBeforeHash, reviewAfterHash) - if err != nil { - return nil, err - } - reviewsList = strings.Split(strings.Replace(newReviewsRaw, - "/", "", -1), "\n") - } - } - - if getAllComs { - hash, err := repo.GetCommitHash( - "notes/" + remote + "/devtools/discuss") - // It is possible that even after we've pulled that this ref still - // isn't present (because there are no comments yet). - if err == nil { - rvws, err := repo.runGitCommand("ls-tree", "-r", "--name-only", - hash) - if err != nil { - return nil, err - } - commentsList = strings.Split(strings.Replace(rvws, "/", "", -1), - "\n") - } - } else { - commentAfterHash, err := repo.GetCommitHash( - "notes/" + remote + "/devtools/discuss") - if err != nil { - return nil, err - } - - // Only run through this if the fetch fetched new revisions. - // Otherwise leave commentsList as its default value, an empty slice - // of strings. - if commentBeforeHash != commentAfterHash { - newCommentsRaw, err := repo.runGitCommand("diff", "--name-only", - commentBeforeHash, commentAfterHash) - if err != nil { - return nil, err - } - commentsList = strings.Split(strings.Replace(newCommentsRaw, - "/", "", -1), "\n") - } - } - - // Now that we have our two lists, we need to merge them. - updatedReviewSet := make(map[string]struct{}) - for _, hash := range append(reviewsList, commentsList...) { - updatedReviewSet[hash] = struct{}{} - } - - updatedReviews := make([]string, 0, len(updatedReviewSet)) - for key, _ := range updatedReviewSet { - updatedReviews = append(updatedReviews, key) - } - return updatedReviews, nil -} diff --git a/third_party/go/git-appraise/repository/git_test.go b/third_party/go/git-appraise/repository/git_test.go deleted file mode 100644 index e1a9e2b2eace..000000000000 --- a/third_party/go/git-appraise/repository/git_test.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2016 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repository - -import ( - "bytes" - "testing" -) - -const ( - simpleBatchCheckOutput = `ddbdcb9d5aa71d35de481789bacece9a2f8138d0 commit -de9ebcdf2a1e93365eefc2739f73f2c68a280c11 commit -def9abf52f9a17d4f168e05bc420557a87a55961 commit -df324616ea2bc9bf6fc7025fc80a373ecec687b6 missing -dfdd159c9c11c08d84c8c050d2a1a4db29147916 commit -e4e48e2b4d76ac305cf76fee1d1c8c0283127d71 commit -e6ae4ed08704fe3c258ab486b07a36e28c3c238a commit -e807a993d1807b154294b9875b9d926b6f246d0c commit -e90f75882526e9bc5a71af64d60ea50092ed0b1d commit` - simpleBatchCatFileOutput = `c1f5a5f135b171cc963b822d338000d185f1ae4f -342 -{"timestamp":"1450315153","v":0,"agent":"Jenkins(1.627) GitNotesJobLogger","url":"https://jenkins-dot-developer-tools-bundle.appspot.com/job/git-appraise/105/"} - -{"timestamp":"1450315161","v":0,"agent":"Jenkins(1.627) GitNotesJobLogger","url":"https://jenkins-dot-developer-tools-bundle.appspot.com/job/git-appraise/105/","status":"success"} - -31ea4952450bbe5db0d6a7a7903e451925106c0f -141 -{"timestamp":"1440202534","url":"https://travis-ci.org/google/git-appraise/builds/76722074","agent":"continuous-integration/travis-ci/push"} - -bde25250a9f6dc9c56f16befa5a2d73c8558b472 -342 -{"timestamp":"1450434854","v":0,"agent":"Jenkins(1.627) GitNotesJobLogger","url":"https://jenkins-dot-developer-tools-bundle.appspot.com/job/git-appraise/112/"} - -{"timestamp":"1450434860","v":0,"agent":"Jenkins(1.627) GitNotesJobLogger","url":"https://jenkins-dot-developer-tools-bundle.appspot.com/job/git-appraise/112/","status":"success"} - -3128dc6881bf7647aea90fef1f4fbf883df6a8fe -342 -{"timestamp":"1457445850","v":0,"agent":"Jenkins(1.627) GitNotesJobLogger","url":"https://jenkins-dot-developer-tools-bundle.appspot.com/job/git-appraise/191/"} - -{"timestamp":"1457445856","v":0,"agent":"Jenkins(1.627) GitNotesJobLogger","url":"https://jenkins-dot-developer-tools-bundle.appspot.com/job/git-appraise/191/","status":"success"} - -` -) - -func TestSplitBatchCheckOutput(t *testing.T) { - buf := bytes.NewBuffer([]byte(simpleBatchCheckOutput)) - commitsMap, err := splitBatchCheckOutput(buf) - if err != nil { - t.Fatal(err) - } - if !commitsMap["ddbdcb9d5aa71d35de481789bacece9a2f8138d0"] { - t.Fatal("Failed to recognize the first commit as valid") - } - if !commitsMap["de9ebcdf2a1e93365eefc2739f73f2c68a280c11"] { - t.Fatal("Failed to recognize the second commit as valid") - } - if !commitsMap["e90f75882526e9bc5a71af64d60ea50092ed0b1d"] { - t.Fatal("Failed to recognize the last commit as valid") - } - if commitsMap["df324616ea2bc9bf6fc7025fc80a373ecec687b6"] { - t.Fatal("Failed to filter out a missing object") - } -} - -func TestSplitBatchCatFileOutput(t *testing.T) { - buf := bytes.NewBuffer([]byte(simpleBatchCatFileOutput)) - notesMap, err := splitBatchCatFileOutput(buf) - if err != nil { - t.Fatal(err) - } - if len(notesMap["c1f5a5f135b171cc963b822d338000d185f1ae4f"]) != 342 { - t.Fatal("Failed to parse the contents of the first cat'ed file") - } - if len(notesMap["31ea4952450bbe5db0d6a7a7903e451925106c0f"]) != 141 { - t.Fatal("Failed to parse the contents of the second cat'ed file") - } - if len(notesMap["3128dc6881bf7647aea90fef1f4fbf883df6a8fe"]) != 342 { - t.Fatal("Failed to parse the contents of the last cat'ed file") - } -} diff --git a/third_party/go/git-appraise/repository/mock_repo.go b/third_party/go/git-appraise/repository/mock_repo.go deleted file mode 100644 index 2d8debe48387..000000000000 --- a/third_party/go/git-appraise/repository/mock_repo.go +++ /dev/null @@ -1,613 +0,0 @@ -/* -Copyright 2015 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repository - -import ( - "crypto/sha1" - "encoding/json" - "fmt" - "strings" -) - -// Constants used for testing. -// We initialize our mock repo with two branches (one of which holds a pending review), -// and commit history that looks like this: -// -// Master Branch: A--B--D--E--F--J -// \ / \ \ -// C \ \ -// \ \ -// Review Branch: G--H--I -// -// Where commits "B" and "D" represent reviews that have been submitted, and "G" -// is a pending review. -const ( - TestTargetRef = "refs/heads/master" - TestReviewRef = "refs/heads/ojarjur/mychange" - TestAlternateReviewRef = "refs/review/mychange" - TestRequestsRef = "refs/notes/devtools/reviews" - TestCommentsRef = "refs/notes/devtools/discuss" - - TestCommitA = "A" - TestCommitB = "B" - TestCommitC = "C" - TestCommitD = "D" - TestCommitE = "E" - TestCommitF = "F" - TestCommitG = "G" - TestCommitH = "H" - TestCommitI = "I" - TestCommitJ = "J" - - TestRequestB = `{"timestamp": "0000000001", "reviewRef": "refs/heads/ojarjur/mychange", "targetRef": "refs/heads/master", "requester": "ojarjur", "reviewers": ["ojarjur"], "description": "B"}` - TestRequestD = `{"timestamp": "0000000002", "reviewRef": "refs/heads/ojarjur/mychange", "targetRef": "refs/heads/master", "requester": "ojarjur", "reviewers": ["ojarjur"], "description": "D"}` - TestRequestG = `{"timestamp": "0000000004", "reviewRef": "refs/heads/ojarjur/mychange", "targetRef": "refs/heads/master", "requester": "ojarjur", "reviewers": ["ojarjur"], "description": "G"} - -{"timestamp": "0000000005", "reviewRef": "refs/heads/ojarjur/mychange", "targetRef": "refs/heads/master", "requester": "ojarjur", "reviewers": ["ojarjur"], "description": "Updated description of G"} - -{"timestamp": "0000000005", "reviewRef": "refs/heads/ojarjur/mychange", "targetRef": "refs/heads/master", "requester": "ojarjur", "reviewers": ["ojarjur"], "description": "Final description of G"}` - - TestDiscussB = `{"timestamp": "0000000001", "author": "ojarjur", "location": {"commit": "B"}, "resolved": true}` - TestDiscussD = `{"timestamp": "0000000003", "author": "ojarjur", "location": {"commit": "E"}, "resolved": true}` -) - -type mockCommit struct { - Message string `json:"message,omitempty"` - Time string `json:"time,omitempty"` - Parents []string `json:"parents,omitempty"` -} - -// mockRepoForTest defines an instance of Repo that can be used for testing. -type mockRepoForTest struct { - Head string - Refs map[string]string `json:"refs,omitempty"` - Commits map[string]mockCommit `json:"commits,omitempty"` - Notes map[string]map[string]string `json:"notes,omitempty"` -} - -func (r *mockRepoForTest) createCommit(message string, time string, parents []string) (string, error) { - newCommit := mockCommit{ - Message: message, - Time: time, - Parents: parents, - } - newCommitJSON, err := json.Marshal(newCommit) - if err != nil { - return "", err - } - newCommitHash := fmt.Sprintf("%x", sha1.Sum([]byte(newCommitJSON))) - r.Commits[newCommitHash] = newCommit - return newCommitHash, nil -} - -// NewMockRepoForTest returns a mocked-out instance of the Repo interface that has been pre-populated with test data. -func NewMockRepoForTest() Repo { - commitA := mockCommit{ - Message: "First commit", - Time: "0", - Parents: nil, - } - commitB := mockCommit{ - Message: "Second commit", - Time: "1", - Parents: []string{TestCommitA}, - } - commitC := mockCommit{ - Message: "No, I'm the second commit", - Time: "1", - Parents: []string{TestCommitA}, - } - commitD := mockCommit{ - Message: "Fourth commit", - Time: "2", - Parents: []string{TestCommitB, TestCommitC}, - } - commitE := mockCommit{ - Message: "Fifth commit", - Time: "3", - Parents: []string{TestCommitD}, - } - commitF := mockCommit{ - Message: "Sixth commit", - Time: "4", - Parents: []string{TestCommitE}, - } - commitG := mockCommit{ - Message: "No, I'm the sixth commit", - Time: "4", - Parents: []string{TestCommitE}, - } - commitH := mockCommit{ - Message: "Seventh commit", - Time: "5", - Parents: []string{TestCommitG, TestCommitF}, - } - commitI := mockCommit{ - Message: "Eighth commit", - Time: "6", - Parents: []string{TestCommitH}, - } - commitJ := mockCommit{ - Message: "No, I'm the eighth commit", - Time: "6", - Parents: []string{TestCommitF}, - } - return &mockRepoForTest{ - Head: TestTargetRef, - Refs: map[string]string{ - TestTargetRef: TestCommitJ, - TestReviewRef: TestCommitI, - TestAlternateReviewRef: TestCommitI, - }, - Commits: map[string]mockCommit{ - TestCommitA: commitA, - TestCommitB: commitB, - TestCommitC: commitC, - TestCommitD: commitD, - TestCommitE: commitE, - TestCommitF: commitF, - TestCommitG: commitG, - TestCommitH: commitH, - TestCommitI: commitI, - TestCommitJ: commitJ, - }, - Notes: map[string]map[string]string{ - TestRequestsRef: map[string]string{ - TestCommitB: TestRequestB, - TestCommitD: TestRequestD, - TestCommitG: TestRequestG, - }, - TestCommentsRef: map[string]string{ - TestCommitB: TestDiscussB, - TestCommitD: TestDiscussD, - }, - }, - } -} - -// GetPath returns the path to the repo. -func (r *mockRepoForTest) GetPath() string { return "~/mockRepo/" } - -// GetRepoStateHash returns a hash which embodies the entire current state of a repository. -func (r *mockRepoForTest) GetRepoStateHash() (string, error) { - repoJSON, err := json.Marshal(r) - if err != nil { - return "", err - } - return fmt.Sprintf("%x", sha1.Sum([]byte(repoJSON))), nil -} - -// GetUserEmail returns the email address that the user has used to configure git. -func (r *mockRepoForTest) GetUserEmail() (string, error) { return "user@example.com", nil } - -// GetUserSigningKey returns the key id the user has configured for -// sigining git artifacts. -func (r *mockRepoForTest) GetUserSigningKey() (string, error) { - return "gpgsig", nil -} - -// GetCoreEditor returns the name of the editor that the user has used to configure git. -func (r *mockRepoForTest) GetCoreEditor() (string, error) { return "vi", nil } - -// GetSubmitStrategy returns the way in which a review is submitted -func (r *mockRepoForTest) GetSubmitStrategy() (string, error) { return "merge", nil } - -// HasUncommittedChanges returns true if there are local, uncommitted changes. -func (r *mockRepoForTest) HasUncommittedChanges() (bool, error) { return false, nil } - -func (r *mockRepoForTest) resolveLocalRef(ref string) (string, error) { - if ref == "HEAD" { - ref = r.Head - } - if commit, ok := r.Refs[ref]; ok { - return commit, nil - } - if _, ok := r.Commits[ref]; ok { - return ref, nil - } - return "", fmt.Errorf("The ref %q does not exist", ref) -} - -// VerifyCommit verifies that the supplied hash points to a known commit. -func (r *mockRepoForTest) VerifyCommit(hash string) error { - if _, ok := r.Commits[hash]; !ok { - return fmt.Errorf("The given hash %q is not a known commit", hash) - } - return nil -} - -// VerifyGitRef verifies that the supplied ref points to a known commit. -func (r *mockRepoForTest) VerifyGitRef(ref string) error { - _, err := r.resolveLocalRef(ref) - return err -} - -// GetHeadRef returns the ref that is the current HEAD. -func (r *mockRepoForTest) GetHeadRef() (string, error) { return r.Head, nil } - -// GetCommitHash returns the hash of the commit pointed to by the given ref. -func (r *mockRepoForTest) GetCommitHash(ref string) (string, error) { - err := r.VerifyGitRef(ref) - if err != nil { - return "", err - } - return r.resolveLocalRef(ref) -} - -// ResolveRefCommit returns the commit pointed to by the given ref, which may be a remote ref. -// -// This differs from GetCommitHash which only works on exact matches, in that it will try to -// intelligently handle the scenario of a ref not existing locally, but being known to exist -// in a remote repo. -// -// This method should be used when a command may be performed by either the reviewer or the -// reviewee, while GetCommitHash should be used when the encompassing command should only be -// performed by the reviewee. -func (r *mockRepoForTest) ResolveRefCommit(ref string) (string, error) { - if commit, err := r.resolveLocalRef(ref); err == nil { - return commit, err - } - return r.resolveLocalRef(strings.Replace(ref, "refs/heads/", "refs/remotes/origin/", 1)) -} - -func (r *mockRepoForTest) getCommit(ref string) (mockCommit, error) { - commit, err := r.resolveLocalRef(ref) - return r.Commits[commit], err -} - -// GetCommitMessage returns the message stored in the commit pointed to by the given ref. -func (r *mockRepoForTest) GetCommitMessage(ref string) (string, error) { - commit, err := r.getCommit(ref) - if err != nil { - return "", err - } - return commit.Message, nil -} - -// GetCommitTime returns the commit time of the commit pointed to by the given ref. -func (r *mockRepoForTest) GetCommitTime(ref string) (string, error) { - commit, err := r.getCommit(ref) - if err != nil { - return "", err - } - return commit.Time, nil -} - -// GetLastParent returns the last parent of the given commit (as ordered by git). -func (r *mockRepoForTest) GetLastParent(ref string) (string, error) { - commit, err := r.getCommit(ref) - if len(commit.Parents) > 0 { - return commit.Parents[len(commit.Parents)-1], err - } - return "", err -} - -// GetCommitDetails returns the details of a commit's metadata. -func (r *mockRepoForTest) GetCommitDetails(ref string) (*CommitDetails, error) { - commit, err := r.getCommit(ref) - if err != nil { - return nil, err - } - var details CommitDetails - details.Author = "Test Author" - details.AuthorEmail = "author@example.com" - details.Summary = commit.Message - details.Time = commit.Time - details.Parents = commit.Parents - return &details, nil -} - -// ancestors returns the breadth-first traversal of a commit's ancestors -func (r *mockRepoForTest) ancestors(commit string) ([]string, error) { - queue := []string{commit} - var ancestors []string - for queue != nil { - var nextQueue []string - for _, c := range queue { - commit, err := r.getCommit(c) - if err != nil { - return nil, err - } - parents := commit.Parents - nextQueue = append(nextQueue, parents...) - ancestors = append(ancestors, parents...) - } - queue = nextQueue - } - return ancestors, nil -} - -// IsAncestor determines if the first argument points to a commit that is an ancestor of the second. -func (r *mockRepoForTest) IsAncestor(ancestor, descendant string) (bool, error) { - var err error - ancestor, err = r.resolveLocalRef(ancestor) - if err != nil { - return false, err - } - descendant, err = r.resolveLocalRef(descendant) - if err != nil { - return false, err - } - if ancestor == descendant { - return true, nil - } - descendantCommit, err := r.getCommit(descendant) - if err != nil { - return false, err - } - for _, parent := range descendantCommit.Parents { - if t, e := r.IsAncestor(ancestor, parent); e == nil && t { - return true, nil - } - } - return false, nil -} - -// MergeBase determines if the first commit that is an ancestor of the two arguments. -func (r *mockRepoForTest) MergeBase(a, b string) (string, error) { - ancestors, err := r.ancestors(a) - if err != nil { - return "", err - } - for _, ancestor := range ancestors { - if t, e := r.IsAncestor(ancestor, b); e == nil && t { - return ancestor, nil - } - } - return "", nil -} - -// Diff computes the diff between two given commits. -func (r *mockRepoForTest) Diff(left, right string, diffArgs ...string) (string, error) { - return fmt.Sprintf("Diff between %q and %q", left, right), nil -} - -// Show returns the contents of the given file at the given commit. -func (r *mockRepoForTest) Show(commit, path string) (string, error) { - return fmt.Sprintf("%s:%s", commit, path), nil -} - -// SwitchToRef changes the currently-checked-out ref. -func (r *mockRepoForTest) SwitchToRef(ref string) error { - r.Head = ref - return nil -} - -// ArchiveRef adds the current commit pointed to by the 'ref' argument -// under the ref specified in the 'archive' argument. -// -// Both the 'ref' and 'archive' arguments are expected to be the fully -// qualified names of git refs (e.g. 'refs/heads/my-change' or -// 'refs/archive/devtools'). -// -// If the ref pointed to by the 'archive' argument does not exist -// yet, then it will be created. -func (r *mockRepoForTest) ArchiveRef(ref, archive string) error { - commitToArchive, err := r.resolveLocalRef(ref) - if err != nil { - return err - } - var archiveParents []string - if archiveCommit, err := r.resolveLocalRef(archive); err == nil { - archiveParents = []string{archiveCommit, commitToArchive} - } else { - archiveParents = []string{commitToArchive} - } - archiveCommit, err := r.createCommit("Archiving", "Nowish", archiveParents) - if err != nil { - return err - } - r.Refs[archive] = archiveCommit - return nil -} - -// MergeRef merges the given ref into the current one. -// -// The ref argument is the ref to merge, and fastForward indicates that the -// current ref should only move forward, as opposed to creating a bubble merge. -func (r *mockRepoForTest) MergeRef(ref string, fastForward bool, messages ...string) error { - newCommitHash, err := r.resolveLocalRef(ref) - if err != nil { - return err - } - if !fastForward { - origCommit, err := r.resolveLocalRef(r.Head) - if err != nil { - return err - } - newCommit, err := r.getCommit(ref) - if err != nil { - return err - } - message := strings.Join(messages, "\n\n") - time := newCommit.Time - parents := []string{origCommit, newCommitHash} - newCommitHash, err = r.createCommit(message, time, parents) - if err != nil { - return err - } - } - r.Refs[r.Head] = newCommitHash - return nil -} - -// MergeAndSignRef merges the given ref into the current one and signs the -// merge. -// -// The ref argument is the ref to merge, and fastForward indicates that the -// current ref should only move forward, as opposed to creating a bubble merge. -func (r *mockRepoForTest) MergeAndSignRef(ref string, fastForward bool, - messages ...string) error { - return nil -} - -// RebaseRef rebases the current ref onto the given one. -func (r *mockRepoForTest) RebaseRef(ref string) error { - parentHash := r.Refs[ref] - origCommit, err := r.getCommit(r.Head) - if err != nil { - return err - } - newCommitHash, err := r.createCommit(origCommit.Message, origCommit.Time, []string{parentHash}) - if err != nil { - return err - } - if strings.HasPrefix(r.Head, "refs/heads/") { - r.Refs[r.Head] = newCommitHash - } else { - // The current head is not a branch, so updating - // it should leave us in a detached-head state. - r.Head = newCommitHash - } - return nil -} - -// RebaseAndSignRef rebases the current ref onto the given one and signs the -// result. -func (r *mockRepoForTest) RebaseAndSignRef(ref string) error { return nil } - -// ListCommits returns the list of commits reachable from the given ref. -// -// The generated list is in chronological order (with the oldest commit first). -// -// If the specified ref does not exist, then this method returns an empty result. -func (r *mockRepoForTest) ListCommits(ref string) []string { return nil } - -// ListCommitsBetween returns the list of commits between the two given revisions. -// -// The "from" parameter is the starting point (exclusive), and the "to" -// parameter is the ending point (inclusive). -// -// The "from" commit does not need to be an ancestor of the "to" commit. If it -// is not, then the merge base of the two is used as the starting point. -// Admittedly, this makes calling these the "between" commits is a bit of a -// misnomer, but it also makes the method easier to use when you want to -// generate the list of changes in a feature branch, as it eliminates the need -// to explicitly calculate the merge base. This also makes the semantics of the -// method compatible with git's built-in "rev-list" command. -// -// The generated list is in chronological order (with the oldest commit first). -func (r *mockRepoForTest) ListCommitsBetween(from, to string) ([]string, error) { - commits := []string{to} - potentialCommits, _ := r.ancestors(to) - for _, commit := range potentialCommits { - blocked, err := r.IsAncestor(commit, from) - if err != nil { - return nil, err - } - if !blocked { - commits = append(commits, commit) - } - } - return commits, nil -} - -// GetNotes reads the notes from the given ref that annotate the given revision. -func (r *mockRepoForTest) GetNotes(notesRef, revision string) []Note { - notesText := r.Notes[notesRef][revision] - var notes []Note - for _, line := range strings.Split(notesText, "\n") { - notes = append(notes, Note(line)) - } - return notes -} - -// GetAllNotes reads the contents of the notes under the given ref for every commit. -// -// The returned value is a mapping from commit hash to the list of notes for that commit. -// -// This is the batch version of the corresponding GetNotes(...) method. -func (r *mockRepoForTest) GetAllNotes(notesRef string) (map[string][]Note, error) { - notesMap := make(map[string][]Note) - for _, commit := range r.ListNotedRevisions(notesRef) { - notesMap[commit] = r.GetNotes(notesRef, commit) - } - return notesMap, nil -} - -// AppendNote appends a note to a revision under the given ref. -func (r *mockRepoForTest) AppendNote(ref, revision string, note Note) error { - existingNotes := r.Notes[ref][revision] - newNotes := existingNotes + "\n" + string(note) - r.Notes[ref][revision] = newNotes - return nil -} - -// ListNotedRevisions returns the collection of revisions that are annotated by notes in the given ref. -func (r *mockRepoForTest) ListNotedRevisions(notesRef string) []string { - var revisions []string - for revision := range r.Notes[notesRef] { - if _, ok := r.Commits[revision]; ok { - revisions = append(revisions, revision) - } - } - return revisions -} - -// PushNotes pushes git notes to a remote repo. -func (r *mockRepoForTest) PushNotes(remote, notesRefPattern string) error { return nil } - -// PullNotes fetches the contents of the given notes ref from a remote repo, -// and then merges them with the corresponding local notes using the -// "cat_sort_uniq" strategy. -func (r *mockRepoForTest) PullNotes(remote, notesRefPattern string) error { return nil } - -// PushNotesAndArchive pushes the given notes and archive refs to a remote repo. -func (r *mockRepoForTest) PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error { - return nil -} - -// PullNotesAndArchive fetches the contents of the notes and archives refs from -// a remote repo, and merges them with the corresponding local refs. -// -// For notes refs, we assume that every note can be automatically merged using -// the 'cat_sort_uniq' strategy (the git-appraise schemas fit that requirement), -// so we automatically merge the remote notes into the local notes. -// -// For "archive" refs, they are expected to be used solely for maintaining -// reachability of commits that are part of the history of any reviews, -// so we do not maintain any consistency with their tree objects. Instead, -// we merely ensure that their history graph includes every commit that we -// intend to keep. -func (r *mockRepoForTest) PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error { - return nil -} - -// MergeNotes merges in the remote's state of the archives reference into -// the local repository's. -func (repo *mockRepoForTest) MergeNotes(remote, notesRefPattern string) error { - return nil -} - -// MergeArchives merges in the remote's state of the archives reference into -// the local repository's. -func (repo *mockRepoForTest) MergeArchives(remote, - archiveRefPattern string) error { - return nil -} - -// FetchAndReturnNewReviewHashes fetches the notes "branches" and then susses -// out the IDs (the revision the review points to) of any new reviews, then -// returns that list of IDs. -// -// This is accomplished by determining which files in the notes tree have -// changed because the _names_ of these files correspond to the revisions they -// point to. -func (repo *mockRepoForTest) FetchAndReturnNewReviewHashes(remote, notesRefPattern, - archiveRefPattern string) ([]string, error) { - return nil, nil -} diff --git a/third_party/go/git-appraise/repository/repo.go b/third_party/go/git-appraise/repository/repo.go deleted file mode 100644 index 91acd177edf0..000000000000 --- a/third_party/go/git-appraise/repository/repo.go +++ /dev/null @@ -1,221 +0,0 @@ -/* -Copyright 2015 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package repository contains helper methods for working with a Git repo. -package repository - -// Note represents the contents of a git-note -type Note []byte - -// CommitDetails represents the contents of a commit. -type CommitDetails struct { - Author string `json:"author,omitempty"` - AuthorEmail string `json:"authorEmail,omitempty"` - Tree string `json:"tree,omitempty"` - Time string `json:"time,omitempty"` - Parents []string `json:"parents,omitempty"` - Summary string `json:"summary,omitempty"` -} - -// Repo represents a source code repository. -type Repo interface { - // GetPath returns the path to the repo. - GetPath() string - - // GetRepoStateHash returns a hash which embodies the entire current state of a repository. - GetRepoStateHash() (string, error) - - // GetUserEmail returns the email address that the user has used to configure git. - GetUserEmail() (string, error) - - // GetUserSigningKey returns the key id the user has configured for - // sigining git artifacts. - GetUserSigningKey() (string, error) - - // GetCoreEditor returns the name of the editor that the user has used to configure git. - GetCoreEditor() (string, error) - - // GetSubmitStrategy returns the way in which a review is submitted - GetSubmitStrategy() (string, error) - - // HasUncommittedChanges returns true if there are local, uncommitted changes. - HasUncommittedChanges() (bool, error) - - // VerifyCommit verifies that the supplied hash points to a known commit. - VerifyCommit(hash string) error - - // VerifyGitRef verifies that the supplied ref points to a known commit. - VerifyGitRef(ref string) error - - // GetHeadRef returns the ref that is the current HEAD. - GetHeadRef() (string, error) - - // GetCommitHash returns the hash of the commit pointed to by the given ref. - GetCommitHash(ref string) (string, error) - - // ResolveRefCommit returns the commit pointed to by the given ref, which may be a remote ref. - // - // This differs from GetCommitHash which only works on exact matches, in that it will try to - // intelligently handle the scenario of a ref not existing locally, but being known to exist - // in a remote repo. - // - // This method should be used when a command may be performed by either the reviewer or the - // reviewee, while GetCommitHash should be used when the encompassing command should only be - // performed by the reviewee. - ResolveRefCommit(ref string) (string, error) - - // GetCommitMessage returns the message stored in the commit pointed to by the given ref. - GetCommitMessage(ref string) (string, error) - - // GetCommitTime returns the commit time of the commit pointed to by the given ref. - GetCommitTime(ref string) (string, error) - - // GetLastParent returns the last parent of the given commit (as ordered by git). - GetLastParent(ref string) (string, error) - - // GetCommitDetails returns the details of a commit's metadata. - GetCommitDetails(ref string) (*CommitDetails, error) - - // MergeBase determines if the first commit that is an ancestor of the two arguments. - MergeBase(a, b string) (string, error) - - // IsAncestor determines if the first argument points to a commit that is an ancestor of the second. - IsAncestor(ancestor, descendant string) (bool, error) - - // Diff computes the diff between two given commits. - Diff(left, right string, diffArgs ...string) (string, error) - - // Show returns the contents of the given file at the given commit. - Show(commit, path string) (string, error) - - // SwitchToRef changes the currently-checked-out ref. - SwitchToRef(ref string) error - - // ArchiveRef adds the current commit pointed to by the 'ref' argument - // under the ref specified in the 'archive' argument. - // - // Both the 'ref' and 'archive' arguments are expected to be the fully - // qualified names of git refs (e.g. 'refs/heads/my-change' or - // 'refs/archive/devtools'). - // - // If the ref pointed to by the 'archive' argument does not exist - // yet, then it will be created. - ArchiveRef(ref, archive string) error - - // MergeRef merges the given ref into the current one. - // - // The ref argument is the ref to merge, and fastForward indicates that the - // current ref should only move forward, as opposed to creating a bubble merge. - // The messages argument(s) provide text that should be included in the default - // merge commit message (separated by blank lines). - MergeRef(ref string, fastForward bool, messages ...string) error - - // MergeAndSignRef merges the given ref into the current one and signs the - // merge. - // - // The ref argument is the ref to merge, and fastForward indicates that the - // current ref should only move forward, as opposed to creating a bubble merge. - // The messages argument(s) provide text that should be included in the default - // merge commit message (separated by blank lines). - MergeAndSignRef(ref string, fastForward bool, messages ...string) error - - // RebaseRef rebases the current ref onto the given one. - RebaseRef(ref string) error - - // RebaseAndSignRef rebases the current ref onto the given one and signs - // the result. - RebaseAndSignRef(ref string) error - - // ListCommits returns the list of commits reachable from the given ref. - // - // The generated list is in chronological order (with the oldest commit first). - // - // If the specified ref does not exist, then this method returns an empty result. - ListCommits(ref string) []string - - // ListCommitsBetween returns the list of commits between the two given revisions. - // - // The "from" parameter is the starting point (exclusive), and the "to" - // parameter is the ending point (inclusive). - // - // The "from" commit does not need to be an ancestor of the "to" commit. If it - // is not, then the merge base of the two is used as the starting point. - // Admittedly, this makes calling these the "between" commits is a bit of a - // misnomer, but it also makes the method easier to use when you want to - // generate the list of changes in a feature branch, as it eliminates the need - // to explicitly calculate the merge base. This also makes the semantics of the - // method compatible with git's built-in "rev-list" command. - // - // The generated list is in chronological order (with the oldest commit first). - ListCommitsBetween(from, to string) ([]string, error) - - // GetNotes reads the notes from the given ref that annotate the given revision. - GetNotes(notesRef, revision string) []Note - - // GetAllNotes reads the contents of the notes under the given ref for every commit. - // - // The returned value is a mapping from commit hash to the list of notes for that commit. - // - // This is the batch version of the corresponding GetNotes(...) method. - GetAllNotes(notesRef string) (map[string][]Note, error) - - // AppendNote appends a note to a revision under the given ref. - AppendNote(ref, revision string, note Note) error - - // ListNotedRevisions returns the collection of revisions that are annotated by notes in the given ref. - ListNotedRevisions(notesRef string) []string - - // PushNotes pushes git notes to a remote repo. - PushNotes(remote, notesRefPattern string) error - - // PullNotes fetches the contents of the given notes ref from a remote repo, - // and then merges them with the corresponding local notes using the - // "cat_sort_uniq" strategy. - PullNotes(remote, notesRefPattern string) error - - // PushNotesAndArchive pushes the given notes and archive refs to a remote repo. - PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error - - // PullNotesAndArchive fetches the contents of the notes and archives refs from - // a remote repo, and merges them with the corresponding local refs. - // - // For notes refs, we assume that every note can be automatically merged using - // the 'cat_sort_uniq' strategy (the git-appraise schemas fit that requirement), - // so we automatically merge the remote notes into the local notes. - // - // For "archive" refs, they are expected to be used solely for maintaining - // reachability of commits that are part of the history of any reviews, - // so we do not maintain any consistency with their tree objects. Instead, - // we merely ensure that their history graph includes every commit that we - // intend to keep. - PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error - - // MergeNotes merges in the remote's state of the archives reference into - // the local repository's. - MergeNotes(remote, notesRefPattern string) error - // MergeArchives merges in the remote's state of the archives reference - // into the local repository's. - MergeArchives(remote, archiveRefPattern string) error - - // FetchAndReturnNewReviewHashes fetches the notes "branches" and then - // susses out the IDs (the revision the review points to) of any new - // reviews, then returns that list of IDs. - // - // This is accomplished by determining which files in the notes tree have - // changed because the _names_ of these files correspond to the revisions - // they point to. - FetchAndReturnNewReviewHashes(remote, notesRefPattern, archiveRefPattern string) ([]string, error) -} |