diff options
Diffstat (limited to 'third_party')
40 files changed, 0 insertions, 7321 deletions
diff --git a/third_party/go/git-appraise/.gitignore b/third_party/go/git-appraise/.gitignore deleted file mode 100644 index 385b6eee948f..000000000000 --- a/third_party/go/git-appraise/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*~ -bin/ diff --git a/third_party/go/git-appraise/CONTRIBUTING.md b/third_party/go/git-appraise/CONTRIBUTING.md deleted file mode 100644 index 8532a3336e18..000000000000 --- a/third_party/go/git-appraise/CONTRIBUTING.md +++ /dev/null @@ -1,24 +0,0 @@ -Want to contribute? Great! First, read this page (including the small print at the end). - -### Before you contribute -Before we can use your code, you must sign the -[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) -(CLA), which you can do online. The CLA is necessary mainly because you own the -copyright to your changes, even after your contribution becomes part of our -codebase. Therefore, we need your permission to use and distribute your code. -We also need to be sure of various other things—for instance that you'll tell -us if you know that your code infringes on other people's patents. You don't -have to sign the CLA until after you've submitted your code for review and a -member has approved it, but you must do it before we can put your code into our -codebase. Before you start working on a larger contribution, you should get in -touch with us first through the issue tracker with your idea so that we can -help out and possibly guide you. Coordinating up front avoids frustrations later. - -### Code reviews -All submissions, including submissions by project members, require review. You -may use a Github pull request to start such a review, but the review itself -will be conducted using this tool. - -### The small print -Contributions made by corporations are covered by a different agreement than -the one above, the Software Grant and Corporate Contributor License Agreement. \ No newline at end of file diff --git a/third_party/go/git-appraise/LICENSE b/third_party/go/git-appraise/LICENSE deleted file mode 100644 index d64569567334..000000000000 --- a/third_party/go/git-appraise/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/third_party/go/git-appraise/README.md b/third_party/go/git-appraise/README.md deleted file mode 100644 index 0227c41366e0..000000000000 --- a/third_party/go/git-appraise/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# Distributed Code Review For Git -[![Build Status](https://travis-ci.org/google/git-appraise.svg?branch=master)](https://travis-ci.org/google/git-appraise) - -This repo contains a command line tool for performing code reviews on git -repositories. - -## Overview - -This tool is a *distributed* code review system for git repos. - -By "distributed", we mean that code reviews are stored inside of the repository -as git objects. Every developer on your team has their own copy of the review -history that they can push or pull. When pulling, updates from the remote -repo are automatically merged by the tool. - -This design removes the need for any sort of server-side setup. As a result, -this tool can work with any git hosting provider, and the only setup required -is installing the client on your workstation. - -## Installation - -Assuming you have the [Go tools installed](https://golang.org/doc/install), run -the following command: - - go get github.com/google/git-appraise/git-appraise - -Then, either make sure that `${GOPATH}/bin` is in your PATH, or explicitly add the -"appraise" git alias by running the following command. - - git config --global alias.appraise '!'"${GOPATH}/bin/git-appraise" - -#### Windows: - - git config --global alias.appraise "!%GOPATH%/bin/git-appraise.exe" - -## Requirements - -This tool expects to run in an environment with the following attributes: - -1. The git command line tool is installed, and included in the PATH. -2. The tool is run from within a git repo. -3. The git command line tool is configured with the credentials it needs to - push to and pull from the remote repos. - -## Usage - -Requesting a code review: - - git appraise request - -Pushing code reviews to a remote: - - git appraise push [<remote>] - -Pulling code reviews from a remote: - - git appraise pull [<remote>] - -Listing open code reviews: - - git appraise list - -Showing the status of the current review, including comments: - - git appraise show - -Showing the diff of a review: - - git appraise show --diff [--diff-opts "<diff-options>"] [<review-hash>] - -Commenting on a review: - - git appraise comment -m "<message>" [-f <file> [-l <line>]] [<review-hash>] - -Accepting the changes in a review: - - git appraise accept [-m "<message>"] [<review-hash>] - -Submitting the current review: - - git appraise submit [--merge | --rebase] - -A more detailed getting started doc is available [here](docs/tutorial.md). - -## Metadata - -The code review data is stored in [git-notes](https://git-scm.com/docs/git-notes), -using the formats described below. Each item stored is written as a single -line of JSON, and is written with at most one such item per line. This allows -the git notes to be automatically merged using the "cat\_sort\_uniq" strategy. - -Since these notes are not in a human-friendly form, all of the refs used to -track them start with the prefix "refs/notes/devtools". This helps make it -clear that these are meant to be read and written by automated tools. - -When a field named "v" appears in one of these notes, it is used to denote -the version of the metadata format being used. If that field is missing, then -it defaults to the value 0, which corresponds to this initial version of the -formats. - -### Code Review Requests - -Code review requests are stored in the "refs/notes/devtools/reviews" ref, and -annotate the first revision in a review. They must conform to the -[request schema](schema/request.json). - -If there are multiple requests for a single commit, then they are sorted by -timestamp and the final request is treated as the current one. This sorting -should be done in a stable manner, so that if there are multiple requests -with the same timestamp, then the last such request in the note is treated -as the current one. - -This design allows a user to update a review request by re-running the -`git appraise request` command. - -### Continuous Integration Status - -Continuous integration build and test results are stored in the -"refs/notes/devtools/ci" ref, and annotate the revision that was built and -tested. They must conform to the [ci schema](schema/ci.json). - -### Robot Comments - -Robot comments are comments generated by static analysis tools. These are -stored in the "refs/notes/devtools/analyses" ref, and annotate the revision. -They must conform to the [analysis schema](schema/analysis.json). - -### Review Comments - -Review comments are comments that were written by a person rather than by a -machine. These are stored in the "refs/notes/devtools/discuss" ref, and -annotate the first revision in the review. They must conform to the -[comment schema](schema/comment.json). - -## Integrations - -### Libraries - - - [Go (use git-appraise itself)](https://github.com/google/git-appraise/blob/master/review/review.go) - - [Rust](https://github.com/Nemo157/git-appraise-rs) - -### Graphical User Interfaces - - - [Git-Appraise-Web](https://github.com/google/git-appraise-web) - -### Plugins - - - [Eclipse](https://github.com/google/git-appraise-eclipse) - - [Jenkins](https://github.com/jenkinsci/google-git-notes-publisher-plugin) - -### Mirrors to other systems - - - [GitHub Pull Requests](https://github.com/google/git-pull-request-mirror) - - [Phabricator Revisions](https://github.com/google/git-phabricator-mirror) - -## Contributing - -Please see [the CONTRIBUTING file](CONTRIBUTING.md) for information on contributing to Git Appraise. diff --git a/third_party/go/git-appraise/commands/abandon.go b/third_party/go/git-appraise/commands/abandon.go deleted file mode 100644 index 6f408e1663c9..000000000000 --- a/third_party/go/git-appraise/commands/abandon.go +++ /dev/null @@ -1,139 +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 commands - -import ( - "errors" - "flag" - "fmt" - - "github.com/google/git-appraise/commands/input" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" - "github.com/google/git-appraise/review/comment" - "github.com/google/git-appraise/review/gpg" - "github.com/google/git-appraise/review/request" -) - -var abandonFlagSet = flag.NewFlagSet("abandon", flag.ExitOnError) - -var ( - abandonMessageFile = abandonFlagSet.String("F", "", "Take the comment from the given file. Use - to read the message from the standard input") - abandonMessage = abandonFlagSet.String("m", "", "Message to attach to the review") - - abandonSign = abandonFlagSet.Bool("S", false, - "Sign the contents of the abandonment") -) - -// abandonReview adds an NMW comment to the current code review. -func abandonReview(repo repository.Repo, args []string) error { - abandonFlagSet.Parse(args) - args = abandonFlagSet.Args() - - var r *review.Review - var err error - if len(args) > 1 { - return errors.New("Only abandon a single review is supported.") - } - - if len(args) == 1 { - r, err = review.Get(repo, args[0]) - } else { - r, err = review.GetCurrent(repo) - } - - if err != nil { - return fmt.Errorf("Failed to load the review: %v\n", err) - } - if r == nil { - return errors.New("There is no matching review.") - } - - if *abandonMessageFile != "" && *abandonMessage == "" { - *abandonMessage, err = input.FromFile(*abandonMessageFile) - if err != nil { - return err - } - } - if *abandonMessageFile == "" && *abandonMessage == "" { - *abandonMessage, err = input.LaunchEditor(repo, commentFilename) - if err != nil { - return err - } - } - - abandonedCommit, err := r.GetHeadCommit() - if err != nil { - return err - } - location := comment.Location{ - Commit: abandonedCommit, - } - resolved := false - userEmail, err := repo.GetUserEmail() - if err != nil { - return err - } - c := comment.New(userEmail, *abandonMessage) - c.Location = &location - c.Resolved = &resolved - - var key string - if *abandonSign { - key, err := repo.GetUserSigningKey() - if err != nil { - return err - } - err = gpg.Sign(key, &c) - if err != nil { - return err - } - } - - err = r.AddComment(c) - if err != nil { - return err - } - - // Empty target ref indicates that request was abandoned - r.Request.TargetRef = "" - // (re)sign the request after clearing out `TargetRef'. - if *abandonSign { - err = gpg.Sign(key, &r.Request) - if err != nil { - return err - } - } - - note, err := r.Request.Write() - if err != nil { - return err - } - - return repo.AppendNote(request.Ref, r.Revision, note) -} - -// abandonCmd defines the "abandon" subcommand. -var abandonCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s abandon [<option>...] [<commit>]\n\nOptions:\n", arg0) - abandonFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return abandonReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/accept.go b/third_party/go/git-appraise/commands/accept.go deleted file mode 100644 index b50f424c252b..000000000000 --- a/third_party/go/git-appraise/commands/accept.go +++ /dev/null @@ -1,109 +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 commands - -import ( - "errors" - "flag" - "fmt" - "github.com/google/git-appraise/commands/input" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" - "github.com/google/git-appraise/review/comment" - "github.com/google/git-appraise/review/gpg" -) - -var acceptFlagSet = flag.NewFlagSet("accept", flag.ExitOnError) - -var ( - acceptMessageFile = acceptFlagSet.String("F", "", "Take the comment from the given file. Use - to read the message from the standard input") - acceptMessage = acceptFlagSet.String("m", "", "Message to attach to the review") - - acceptSign = acceptFlagSet.Bool("S", false, - "sign the contents of the acceptance") -) - -// acceptReview adds an LGTM comment to the current code review. -func acceptReview(repo repository.Repo, args []string) error { - acceptFlagSet.Parse(args) - args = acceptFlagSet.Args() - - var r *review.Review - var err error - if len(args) > 1 { - return errors.New("Only accepting a single review is supported.") - } - - if len(args) == 1 { - r, err = review.Get(repo, args[0]) - } else { - r, err = review.GetCurrent(repo) - } - - if err != nil { - return fmt.Errorf("Failed to load the review: %v\n", err) - } - if r == nil { - return errors.New("There is no matching review.") - } - - acceptedCommit, err := r.GetHeadCommit() - if err != nil { - return err - } - location := comment.Location{ - Commit: acceptedCommit, - } - resolved := true - userEmail, err := repo.GetUserEmail() - if err != nil { - return err - } - - if *acceptMessageFile != "" && *acceptMessage == "" { - *acceptMessage, err = input.FromFile(*acceptMessageFile) - if err != nil { - return err - } - } - - c := comment.New(userEmail, *acceptMessage) - c.Location = &location - c.Resolved = &resolved - if *acceptSign { - key, err := repo.GetUserSigningKey() - if err != nil { - return err - } - err = gpg.Sign(key, &c) - if err != nil { - return err - } - } - return r.AddComment(c) -} - -// acceptCmd defines the "accept" subcommand. -var acceptCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s accept [<option>...] [<commit>]\n\nOptions:\n", arg0) - acceptFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return acceptReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/commands.go b/third_party/go/git-appraise/commands/commands.go deleted file mode 100644 index 75b8c72d3769..000000000000 --- a/third_party/go/git-appraise/commands/commands.go +++ /dev/null @@ -1,55 +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 commands contains the assorted sub commands supported by the git-appraise tool. -package commands - -import ( - "github.com/google/git-appraise/repository" -) - -const notesRefPattern = "refs/notes/devtools/*" -const archiveRefPattern = "refs/devtools/archives/*" -const commentFilename = "APPRAISE_COMMENT_EDITMSG" - -// Command represents the definition of a single command. -type Command struct { - Usage func(string) - RunMethod func(repository.Repo, []string) error -} - -// Run executes a command, given its arguments. -// -// The args parameter is all of the command line args that followed the -// subcommand. -func (cmd *Command) Run(repo repository.Repo, args []string) error { - return cmd.RunMethod(repo, args) -} - -// CommandMap defines all of the available (sub)commands. -var CommandMap = map[string]*Command{ - "abandon": abandonCmd, - "accept": acceptCmd, - "comment": commentCmd, - "list": listCmd, - "pull": pullCmd, - "push": pushCmd, - "rebase": rebaseCmd, - "reject": rejectCmd, - "request": requestCmd, - "show": showCmd, - "submit": submitCmd, -} diff --git a/third_party/go/git-appraise/commands/comment.go b/third_party/go/git-appraise/commands/comment.go deleted file mode 100644 index 554ac6dc78b8..000000000000 --- a/third_party/go/git-appraise/commands/comment.go +++ /dev/null @@ -1,165 +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 commands - -import ( - "errors" - "flag" - "fmt" - - "github.com/google/git-appraise/commands/input" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" - "github.com/google/git-appraise/review/comment" - "github.com/google/git-appraise/review/gpg" -) - -var commentFlagSet = flag.NewFlagSet("comment", flag.ExitOnError) -var commentLocation = comment.Range{} - -var ( - commentMessageFile = commentFlagSet.String("F", "", "Take the comment from the given file. Use - to read the message from the standard input") - commentMessage = commentFlagSet.String("m", "", "Message to attach to the review") - commentParent = commentFlagSet.String("p", "", "Parent comment") - commentFile = commentFlagSet.String("f", "", "File being commented upon") - commentLgtm = commentFlagSet.Bool("lgtm", false, "'Looks Good To Me'. Set this to express your approval. This cannot be combined with nmw") - commentNmw = commentFlagSet.Bool("nmw", false, "'Needs More Work'. Set this to express your disapproval. This cannot be combined with lgtm") - commentSign = commentFlagSet.Bool("S", false, - "Sign the contents of the comment") -) - -func init() { - commentFlagSet.Var(&commentLocation, "l", - `File location to be commented upon; requires that the -f flag also be set. -Location follows the following format: - <START LINE>[+<START COLUMN>][:<END LINE>[+<END COLUMN>]] -So, in order to comment starting on the 5th character of the 2nd line until (and -including) the 4th character of the 7th line, use: - -l 2+5:7+4`) -} - -// commentHashExists checks if the given comment hash exists in the given comment threads. -func commentHashExists(hashToFind string, threads []review.CommentThread) bool { - for _, thread := range threads { - if thread.Hash == hashToFind { - return true - } - if commentHashExists(hashToFind, thread.Children) { - return true - } - } - return false -} - -// commentOnReview adds a comment to the current code review. -func commentOnReview(repo repository.Repo, args []string) error { - commentFlagSet.Parse(args) - args = commentFlagSet.Args() - - var r *review.Review - var err error - if len(args) > 1 { - return errors.New("Only accepting a single review is supported.") - } - - if len(args) == 1 { - r, err = review.Get(repo, args[0]) - } else { - r, err = review.GetCurrent(repo) - } - - if err != nil { - return fmt.Errorf("Failed to load the review: %v\n", err) - } - if r == nil { - return errors.New("There is no matching review.") - } - - if *commentLgtm && *commentNmw { - return errors.New("You cannot combine the flags -lgtm and -nmw.") - } - if commentLocation != (comment.Range{}) && *commentFile == "" { - return errors.New("Specifying a line number with the -l flag requires that you also specify a file name with the -f flag.") - } - if *commentParent != "" && !commentHashExists(*commentParent, r.Comments) { - return errors.New("There is no matching parent comment.") - } - - if *commentMessageFile != "" && *commentMessage == "" { - *commentMessage, err = input.FromFile(*commentMessageFile) - if err != nil { - return err - } - } - if *commentMessageFile == "" && *commentMessage == "" { - *commentMessage, err = input.LaunchEditor(repo, commentFilename) - if err != nil { - return err - } - } - - commentedUponCommit, err := r.GetHeadCommit() - if err != nil { - return err - } - location := comment.Location{ - Commit: commentedUponCommit, - } - if *commentFile != "" { - location.Path = *commentFile - location.Range = &commentLocation - if err := location.Check(r.Repo); err != nil { - return fmt.Errorf("Unable to comment on the given location: %v", err) - } - } - - userEmail, err := repo.GetUserEmail() - if err != nil { - return err - } - c := comment.New(userEmail, *commentMessage) - c.Location = &location - c.Parent = *commentParent - if *commentLgtm || *commentNmw { - resolved := *commentLgtm - c.Resolved = &resolved - } - - if *commentSign { - key, err := repo.GetUserSigningKey() - if err != nil { - return err - } - err = gpg.Sign(key, &c) - if err != nil { - return err - } - } - - return r.AddComment(c) -} - -// commentCmd defines the "comment" subcommand. -var commentCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s comment [<option>...] [<review-hash>]\n\nOptions:\n", arg0) - commentFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return commentOnReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/input/input.go b/third_party/go/git-appraise/commands/input/input.go deleted file mode 100644 index 9a8678a8272e..000000000000 --- a/third_party/go/git-appraise/commands/input/input.go +++ /dev/null @@ -1,118 +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 input - -import ( - "bufio" - "bytes" - "fmt" - "github.com/google/git-appraise/repository" - "io/ioutil" - "os" - "os/exec" -) - -// LaunchEditor launches the default editor configured for the given repo. This -// method blocks until the editor command has returned. -// -// The specified filename should be a temporary file and provided as a relative path -// from the repo (e.g. "FILENAME" will be converted to ".git/FILENAME"). This file -// will be deleted after the editor is closed and its contents have been read. -// -// This method returns the text that was read from the temporary file, or -// an error if any step in the process failed. -func LaunchEditor(repo repository.Repo, fileName string) (string, error) { - editor, err := repo.GetCoreEditor() - if err != nil { - return "", fmt.Errorf("Unable to detect default git editor: %v\n", err) - } - - path := fmt.Sprintf("%s/.git/%s", repo.GetPath(), fileName) - - cmd, err := startInlineCommand(editor, path) - if err != nil { - // Running the editor directly did not work. This might mean that - // the editor string is not a path to an executable, but rather - // a shell command (e.g. "emacsclient --tty"). As such, we'll try - // to run the command through bash, and if that fails, try with sh - args := []string{"-c", fmt.Sprintf("%s %q", editor, path)} - cmd, err = startInlineCommand("bash", args...) - if err != nil { - cmd, err = startInlineCommand("sh", args...) - } - } - if err != nil { - return "", fmt.Errorf("Unable to start editor: %v\n", err) - } - - if err := cmd.Wait(); err != nil { - return "", fmt.Errorf("Editing finished with error: %v\n", err) - } - - output, err := ioutil.ReadFile(path) - if err != nil { - os.Remove(path) - return "", fmt.Errorf("Error reading edited file: %v\n", err) - } - os.Remove(path) - return string(output), err -} - -// FromFile loads and returns the contents of a given file. If - is passed -// through, much like git, it will read from stdin. This can be piped data, -// unless there is a tty in which case the user will be prompted to enter a -// message. -func FromFile(fileName string) (string, error) { - if fileName == "-" { - stat, err := os.Stdin.Stat() - if err != nil { - return "", fmt.Errorf("Error reading from stdin: %v\n", err) - } - if (stat.Mode() & os.ModeCharDevice) == 0 { - // There is no tty. This will allow us to read piped data instead. - output, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return "", fmt.Errorf("Error reading from stdin: %v\n", err) - } - return string(output), err - } - - fmt.Printf("(reading comment from standard input)\n") - var output bytes.Buffer - s := bufio.NewScanner(os.Stdin) - for s.Scan() { - output.Write(s.Bytes()) - output.WriteRune('\n') - } - return output.String(), nil - } - - output, err := ioutil.ReadFile(fileName) - if err != nil { - return "", fmt.Errorf("Error reading file: %v\n", err) - } - return string(output), err -} - -func startInlineCommand(command string, args ...string) (*exec.Cmd, error) { - cmd := exec.Command(command, args...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Start() - return cmd, err -} diff --git a/third_party/go/git-appraise/commands/list.go b/third_party/go/git-appraise/commands/list.go deleted file mode 100644 index cc9338dd7e97..000000000000 --- a/third_party/go/git-appraise/commands/list.go +++ /dev/null @@ -1,74 +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 commands - -import ( - "encoding/json" - "flag" - "fmt" - "github.com/google/git-appraise/commands/output" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" -) - -var listFlagSet = flag.NewFlagSet("list", flag.ExitOnError) - -var ( - listAll = listFlagSet.Bool("a", false, "List all reviews (not just the open ones).") - listJSONOutput = listFlagSet.Bool("json", false, "Format the output as JSON") -) - -// listReviews lists all extant reviews. -// TODO(ojarjur): Add more flags for filtering the output (e.g. filtering by reviewer or status). -func listReviews(repo repository.Repo, args []string) error { - listFlagSet.Parse(args) - var reviews []review.Summary - if *listAll { - reviews = review.ListAll(repo) - if !*listJSONOutput { - fmt.Printf("Loaded %d reviews:\n", len(reviews)) - } - } else { - reviews = review.ListOpen(repo) - if !*listJSONOutput { - fmt.Printf("Loaded %d open reviews:\n", len(reviews)) - } - } - if *listJSONOutput { - b, err := json.MarshalIndent(reviews, "", " ") - if err != nil { - return err - } - fmt.Println(string(b)) - return nil - } - for _, r := range reviews { - output.PrintSummary(&r) - } - return nil -} - -// listCmd defines the "list" subcommand. -var listCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s list [<option>...]\n\nOptions:\n", arg0) - listFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return listReviews(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/output/output.go b/third_party/go/git-appraise/commands/output/output.go deleted file mode 100644 index 4613cd38576b..000000000000 --- a/third_party/go/git-appraise/commands/output/output.go +++ /dev/null @@ -1,216 +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 output contains helper methods for pretty-printing code reviews. -package output - -import ( - "fmt" - "strconv" - "strings" - "time" - - "github.com/google/git-appraise/review" -) - -const ( - // Template for printing the summary of a code review. - reviewSummaryTemplate = `[%s] %.12s - %s -` - // Template for printing the summary of a code review. - reviewDetailsTemplate = ` %q -> %q - reviewers: %q - requester: %q - build status: %s -` - // Template for printing the location of an inline comment - commentLocationTemplate = `%s%q@%.12s -` - // Template for printing a single comment. - commentTemplate = `comment: %s -author: %s -time: %s -status: %s -%s` - // Template for displaying the summary of the comment threads for a review - commentSummaryTemplate = ` comments (%d threads): -` - // Number of lines of context to print for inline comments - contextLineCount = 5 -) - -// getStatusString returns a human friendly string encapsulating both the review's -// resolved status, and its submitted status. -func getStatusString(r *review.Summary) string { - if r.Resolved == nil && r.Submitted { - return "tbr" - } - if r.Resolved == nil { - return "pending" - } - if *r.Resolved && r.Submitted { - return "submitted" - } - if *r.Resolved { - return "accepted" - } - if r.Submitted { - return "danger" - } - if r.Request.TargetRef == "" { - return "abandon" - } - return "rejected" -} - -// PrintSummary prints a single-line summary of a review. -func PrintSummary(r *review.Summary) { - statusString := getStatusString(r) - indentedDescription := strings.Replace(r.Request.Description, "\n", "\n ", -1) - fmt.Printf(reviewSummaryTemplate, statusString, r.Revision, indentedDescription) -} - -// reformatTimestamp takes a timestamp string of the form "0123456789" and changes it -// to the form "Mon Jan _2 13:04:05 UTC 2006". -// -// Timestamps that are not in the format we expect are left alone. -func reformatTimestamp(timestamp string) string { - parsedTimestamp, err := strconv.ParseInt(timestamp, 10, 64) - if err != nil { - // The timestamp is an unexpected format, so leave it alone - return timestamp - } - t := time.Unix(parsedTimestamp, 0) - return t.Format(time.UnixDate) -} - -// showThread prints the detailed output for an entire comment thread. -func showThread(r *review.Review, thread review.CommentThread) error { - comment := thread.Comment - indent := " " - if comment.Location != nil && comment.Location.Path != "" && comment.Location.Range != nil && comment.Location.Range.StartLine > 0 { - contents, err := r.Repo.Show(comment.Location.Commit, comment.Location.Path) - if err != nil { - return err - } - lines := strings.Split(contents, "\n") - err = comment.Location.Check(r.Repo) - if err != nil { - return err - } - if comment.Location.Range.StartLine <= uint32(len(lines)) { - firstLine := comment.Location.Range.StartLine - lastLine := comment.Location.Range.EndLine - - if firstLine == 0 { - firstLine = 1 - } - - if lastLine == 0 { - lastLine = firstLine - } - - if lastLine == firstLine { - minLine := int(lastLine) - int(contextLineCount) - if minLine <= 0 { - minLine = 1 - } - firstLine = uint32(minLine) - } - - fmt.Printf(commentLocationTemplate, indent, comment.Location.Path, comment.Location.Commit) - fmt.Println(indent + "|" + strings.Join(lines[firstLine-1:lastLine], "\n"+indent+"|")) - } - } - return showSubThread(r, thread, indent) -} - -// showSubThread prints the given comment (sub)thread, indented by the given prefix string. -func showSubThread(r *review.Review, thread review.CommentThread, indent string) error { - statusString := "fyi" - if thread.Resolved != nil { - if *thread.Resolved { - statusString = "lgtm" - } else { - statusString = "needs work" - } - } - comment := thread.Comment - threadHash := thread.Hash - timestamp := reformatTimestamp(comment.Timestamp) - commentSummary := fmt.Sprintf(indent+commentTemplate, threadHash, comment.Author, timestamp, statusString, comment.Description) - indent = indent + " " - indentedSummary := strings.Replace(commentSummary, "\n", "\n"+indent, -1) - fmt.Println(indentedSummary) - for _, child := range thread.Children { - err := showSubThread(r, child, indent) - if err != nil { - return err - } - } - return nil -} - -// printAnalyses prints the static analysis results for the latest commit in the review. -func printAnalyses(r *review.Review) { - fmt.Println(" analyses: ", r.GetAnalysesMessage()) -} - -// printComments prints all of the comments for the review, with snippets of the preceding source code. -func printComments(r *review.Review) error { - fmt.Printf(commentSummaryTemplate, len(r.Comments)) - for _, thread := range r.Comments { - err := showThread(r, thread) - if err != nil { - return err - } - } - return nil -} - -// PrintDetails prints a multi-line overview of a review, including all comments. -func PrintDetails(r *review.Review) error { - PrintSummary(r.Summary) - fmt.Printf(reviewDetailsTemplate, r.Request.ReviewRef, r.Request.TargetRef, - strings.Join(r.Request.Reviewers, ", "), - r.Request.Requester, r.GetBuildStatusMessage()) - printAnalyses(r) - if err := printComments(r); err != nil { - return err - } - return nil -} - -// PrintJSON pretty prints the given review in JSON format. -func PrintJSON(r *review.Review) error { - json, err := r.GetJSON() - if err != nil { - return err - } - fmt.Println(json) - return nil -} - -// PrintDiff prints the diff of the review. -func PrintDiff(r *review.Review, diffArgs ...string) error { - diff, err := r.GetDiff(diffArgs...) - if err != nil { - return err - } - fmt.Println(diff) - return nil -} diff --git a/third_party/go/git-appraise/commands/pull.go b/third_party/go/git-appraise/commands/pull.go deleted file mode 100644 index 809c20fdbbfe..000000000000 --- a/third_party/go/git-appraise/commands/pull.go +++ /dev/null @@ -1,93 +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 commands - -import ( - "errors" - "flag" - "fmt" - - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" -) - -var ( - pullFlagSet = flag.NewFlagSet("pull", flag.ExitOnError) - pullVerify = pullFlagSet.Bool("verify-signatures", false, - "verify the signatures of pulled reviews") -) - -// pull updates the local git-notes used for reviews with those from a remote -// repo. -func pull(repo repository.Repo, args []string) error { - pullFlagSet.Parse(args) - pullArgs := pullFlagSet.Args() - - if len(pullArgs) > 1 { - return errors.New( - "Only pulling from one remote at a time is supported.") - } - - remote := "origin" - if len(pullArgs) == 1 { - remote = pullArgs[0] - } - // This is the easy case. We're not checking signatures so just go the - // normal route. - if !*pullVerify { - return repo.PullNotesAndArchive(remote, notesRefPattern, - archiveRefPattern) - } - - // Otherwise, we collect the fetched reviewed revisions (their hashes), get - // their reviews, and then one by one, verify them. If we make it through - // the set, _then_ we merge the remote reference into the local branch. - revisions, err := repo.FetchAndReturnNewReviewHashes(remote, - notesRefPattern, archiveRefPattern) - if err != nil { - return err - } - for _, revision := range revisions { - rvw, err := review.GetSummaryViaRefs(repo, - "refs/notes/"+remote+"/devtools/reviews", - "refs/notes/"+remote+"/devtools/discuss", revision) - if err != nil { - return err - } - err = rvw.Verify() - if err != nil { - return err - } - fmt.Println("verified review:", revision) - } - - err = repo.MergeNotes(remote, notesRefPattern) - if err != nil { - return err - } - return repo.MergeArchives(remote, archiveRefPattern) -} - -var pullCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s pull [<option>] [<remote>]\n\nOptions:\n", arg0) - pullFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return pull(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/push.go b/third_party/go/git-appraise/commands/push.go deleted file mode 100644 index c75a25eac738..000000000000 --- a/third_party/go/git-appraise/commands/push.go +++ /dev/null @@ -1,49 +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 commands - -import ( - "errors" - "fmt" - "github.com/google/git-appraise/repository" -) - -// push pushes the local git-notes used for reviews to a remote repo. -func push(repo repository.Repo, args []string) error { - if len(args) > 1 { - return errors.New("Only pushing to one remote at a time is supported.") - } - - remote := "origin" - if len(args) == 1 { - remote = args[0] - } - - if err := repo.PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern); err != nil { - return err - } - return nil -} - -var pushCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s push [<remote>]\n", arg0) - }, - RunMethod: func(repo repository.Repo, args []string) error { - return push(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/rebase.go b/third_party/go/git-appraise/commands/rebase.go deleted file mode 100644 index 2c4595a57693..000000000000 --- a/third_party/go/git-appraise/commands/rebase.go +++ /dev/null @@ -1,100 +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 commands - -import ( - "errors" - "flag" - "fmt" - - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" -) - -var rebaseFlagSet = flag.NewFlagSet("rebase", flag.ExitOnError) - -var ( - rebaseArchive = rebaseFlagSet.Bool("archive", true, "Prevent the original commit from being garbage collected.") - rebaseSign = rebaseFlagSet.Bool("S", false, - "Sign the contents of the request after the rebase") -) - -// Validate that the user's request to rebase a review makes sense. -// -// This checks both that the request is well formed, and that the -// corresponding review is in a state where rebasing is appropriate. -func validateRebaseRequest(repo repository.Repo, args []string) (*review.Review, error) { - var r *review.Review - var err error - if len(args) > 1 { - return nil, errors.New("Only rebasing a single review is supported.") - } - if len(args) == 1 { - r, err = review.Get(repo, args[0]) - } else { - r, err = review.GetCurrent(repo) - } - if err != nil { - return nil, fmt.Errorf("Failed to load the review: %v\n", err) - } - if r == nil { - return nil, errors.New("There is no matching review.") - } - - if r.Submitted { - return nil, errors.New("The review has already been submitted.") - } - - if r.Request.TargetRef == "" { - return nil, errors.New("The review was abandoned.") - } - - target := r.Request.TargetRef - if err := repo.VerifyGitRef(target); err != nil { - return nil, err - } - - return r, nil -} - -// Rebase the current code review. -// -// The "args" parameter contains all of the command line arguments that followed the subcommand. -func rebaseReview(repo repository.Repo, args []string) error { - rebaseFlagSet.Parse(args) - args = rebaseFlagSet.Args() - - r, err := validateRebaseRequest(repo, args) - if err != nil { - return err - } - if *rebaseSign { - return r.RebaseAndSign(*rebaseArchive) - } - return r.Rebase(*rebaseArchive) -} - -// rebaseCmd defines the "rebase" subcommand. -var rebaseCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s rebase [<option>...] [<review-hash>]\n\nOptions:\n", arg0) - rebaseFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return rebaseReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/reject.go b/third_party/go/git-appraise/commands/reject.go deleted file mode 100644 index e0e45babf8bc..000000000000 --- a/third_party/go/git-appraise/commands/reject.go +++ /dev/null @@ -1,119 +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 commands - -import ( - "errors" - "flag" - "fmt" - - "github.com/google/git-appraise/commands/input" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" - "github.com/google/git-appraise/review/comment" - "github.com/google/git-appraise/review/gpg" -) - -var rejectFlagSet = flag.NewFlagSet("reject", flag.ExitOnError) - -var ( - rejectMessageFile = rejectFlagSet.String("F", "", "Take the comment from the given file. Use - to read the message from the standard input") - rejectMessage = rejectFlagSet.String("m", "", "Message to attach to the review") - - rejectSign = rejectFlagSet.Bool("S", false, - "Sign the contents of the rejection") -) - -// rejectReview adds an NMW comment to the current code review. -func rejectReview(repo repository.Repo, args []string) error { - rejectFlagSet.Parse(args) - args = rejectFlagSet.Args() - - var r *review.Review - var err error - if len(args) > 1 { - return errors.New("Only rejecting a single review is supported.") - } - - if len(args) == 1 { - r, err = review.Get(repo, args[0]) - } else { - r, err = review.GetCurrent(repo) - } - - if err != nil { - return fmt.Errorf("Failed to load the review: %v\n", err) - } - if r == nil { - return errors.New("There is no matching review.") - } - - if r.Request.TargetRef == "" { - return errors.New("The review was abandoned.") - } - - if *rejectMessageFile != "" && *rejectMessage == "" { - *rejectMessage, err = input.FromFile(*rejectMessageFile) - if err != nil { - return err - } - } - if *rejectMessageFile == "" && *rejectMessage == "" { - *rejectMessage, err = input.LaunchEditor(repo, commentFilename) - if err != nil { - return err - } - } - - rejectedCommit, err := r.GetHeadCommit() - if err != nil { - return err - } - location := comment.Location{ - Commit: rejectedCommit, - } - resolved := false - userEmail, err := repo.GetUserEmail() - if err != nil { - return err - } - c := comment.New(userEmail, *rejectMessage) - c.Location = &location - c.Resolved = &resolved - if *rejectSign { - key, err := repo.GetUserSigningKey() - if err != nil { - return err - } - err = gpg.Sign(key, &c) - if err != nil { - return err - } - } - return r.AddComment(c) -} - -// rejectCmd defines the "reject" subcommand. -var rejectCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s reject [<option>...] [<commit>]\n\nOptions:\n", arg0) - rejectFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return rejectReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/request.go b/third_party/go/git-appraise/commands/request.go deleted file mode 100644 index 9a9854c3f8a6..000000000000 --- a/third_party/go/git-appraise/commands/request.go +++ /dev/null @@ -1,182 +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 commands - -import ( - "errors" - "flag" - "fmt" - "strings" - - "github.com/google/git-appraise/commands/input" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review/gpg" - "github.com/google/git-appraise/review/request" -) - -// Template for the "request" subcommand's output. -const requestSummaryTemplate = `Review requested: -Commit: %s -Target Ref: %s -Review Ref: %s -Message: "%s" -` - -var requestFlagSet = flag.NewFlagSet("request", flag.ExitOnError) - -var ( - requestMessageFile = requestFlagSet.String("F", "", "Take the comment from the given file. Use - to read the message from the standard input") - requestMessage = requestFlagSet.String("m", "", "Message to attach to the review") - requestReviewers = requestFlagSet.String("r", "", "Comma-separated list of reviewers") - requestSource = requestFlagSet.String("source", "HEAD", "Revision to review") - requestTarget = requestFlagSet.String("target", "refs/heads/master", "Revision against which to review") - requestQuiet = requestFlagSet.Bool("quiet", false, "Suppress review summary output") - requestAllowUncommitted = requestFlagSet.Bool("allow-uncommitted", false, "Allow uncommitted local changes.") - requestSign = requestFlagSet.Bool("S", false, - "GPG sign the content of the request") -) - -// Build the template review request based solely on the parsed flag values. -func buildRequestFromFlags(requester string) (request.Request, error) { - var reviewers []string - if len(*requestReviewers) > 0 { - for _, reviewer := range strings.Split(*requestReviewers, ",") { - reviewers = append(reviewers, strings.TrimSpace(reviewer)) - } - } - if *requestMessageFile != "" && *requestMessage == "" { - var err error - *requestMessage, err = input.FromFile(*requestMessageFile) - if err != nil { - return request.Request{}, err - } - } - - return request.New(requester, reviewers, *requestSource, *requestTarget, *requestMessage), nil -} - -// Get the commit at which the review request should be anchored. -func getReviewCommit(repo repository.Repo, r request.Request, args []string) (string, string, error) { - if len(args) > 1 { - return "", "", errors.New("Only updating a single review is supported.") - } - if len(args) == 1 { - base, err := repo.MergeBase(r.TargetRef, args[0]) - if err != nil { - return "", "", err - } - return args[0], base, nil - } - - base, err := repo.MergeBase(r.TargetRef, r.ReviewRef) - if err != nil { - return "", "", err - } - reviewCommits, err := repo.ListCommitsBetween(base, r.ReviewRef) - if err != nil { - return "", "", err - } - if reviewCommits == nil { - return "", "", errors.New("There are no commits included in the review request") - } - return reviewCommits[0], base, nil -} - -// Create a new code review request. -// -// The "args" parameter is all of the command line arguments that followed the subcommand. -func requestReview(repo repository.Repo, args []string) error { - requestFlagSet.Parse(args) - args = requestFlagSet.Args() - - if !*requestAllowUncommitted { - // Requesting a code review with uncommited local changes is usually a mistake, so - // we want to report that to the user instead of creating the request. - hasUncommitted, err := repo.HasUncommittedChanges() - if err != nil { - return err - } - if hasUncommitted { - return errors.New("You have uncommitted or untracked files. Use --allow-uncommitted to ignore those.") - } - } - - userEmail, err := repo.GetUserEmail() - if err != nil { - return err - } - r, err := buildRequestFromFlags(userEmail) - if err != nil { - return err - } - if r.ReviewRef == "HEAD" { - headRef, err := repo.GetHeadRef() - if err != nil { - return err - } - r.ReviewRef = headRef - } - if err := repo.VerifyGitRef(r.TargetRef); err != nil { - return err - } - if err := repo.VerifyGitRef(r.ReviewRef); err != nil { - return err - } - - reviewCommit, baseCommit, err := getReviewCommit(repo, r, args) - if err != nil { - return err - } - r.BaseCommit = baseCommit - if r.Description == "" { - description, err := repo.GetCommitMessage(reviewCommit) - if err != nil { - return err - } - r.Description = description - } - if *requestSign { - key, err := repo.GetUserSigningKey() - if err != nil { - return err - } - err = gpg.Sign(key, &r) - if err != nil { - return err - } - } - note, err := r.Write() - if err != nil { - return err - } - repo.AppendNote(request.Ref, reviewCommit, note) - if !*requestQuiet { - fmt.Printf(requestSummaryTemplate, reviewCommit, r.TargetRef, r.ReviewRef, r.Description) - } - return nil -} - -// requestCmd defines the "request" subcommand. -var requestCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s request [<option>...] [<review-hash>]\n\nOptions:\n", arg0) - requestFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return requestReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/request_test.go b/third_party/go/git-appraise/commands/request_test.go deleted file mode 100644 index 3e09892e5760..000000000000 --- a/third_party/go/git-appraise/commands/request_test.go +++ /dev/null @@ -1,36 +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 commands - -import ( - "testing" -) - -func TestBuildRequestFromFlags(t *testing.T) { - args := []string{"-m", "Request message", "-r", "Me, Myself, \nAnd I "} - requestFlagSet.Parse(args) - r, err := buildRequestFromFlags("user@hostname.com") - if err != nil { - t.Fatal(err) - } - if r.Description != "Request message" { - t.Fatalf("Unexpected request description: '%s'", r.Description) - } - if r.Reviewers == nil || len(r.Reviewers) != 3 || r.Reviewers[0] != "Me" || r.Reviewers[1] != "Myself" || r.Reviewers[2] != "And I" { - t.Fatalf("Unexpected reviewers list: '%v'", r.Reviewers) - } -} diff --git a/third_party/go/git-appraise/commands/show.go b/third_party/go/git-appraise/commands/show.go deleted file mode 100644 index 9eb57dd093c7..000000000000 --- a/third_party/go/git-appraise/commands/show.go +++ /dev/null @@ -1,85 +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 commands - -import ( - "errors" - "flag" - "fmt" - "github.com/google/git-appraise/commands/output" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" - "strings" -) - -var showFlagSet = flag.NewFlagSet("show", flag.ExitOnError) - -var ( - showJSONOutput = showFlagSet.Bool("json", false, "Format the output as JSON") - showDiffOutput = showFlagSet.Bool("diff", false, "Show the current diff for the review") - showDiffOptions = showFlagSet.String("diff-opts", "", "Options to pass to the diff tool; can only be used with the --diff option") -) - -// showReview prints the current code review. -func showReview(repo repository.Repo, args []string) error { - showFlagSet.Parse(args) - args = showFlagSet.Args() - if *showDiffOptions != "" && !*showDiffOutput { - return errors.New("The --diff-opts flag can only be used if the --diff flag is set.") - } - - var r *review.Review - var err error - if len(args) > 1 { - return errors.New("Only showing a single review is supported.") - } - - if len(args) == 1 { - r, err = review.Get(repo, args[0]) - } else { - r, err = review.GetCurrent(repo) - } - - if err != nil { - return fmt.Errorf("Failed to load the review: %v\n", err) - } - if r == nil { - return errors.New("There is no matching review.") - } - if *showJSONOutput { - return output.PrintJSON(r) - } - if *showDiffOutput { - var diffArgs []string - if *showDiffOptions != "" { - diffArgs = strings.Split(*showDiffOptions, ",") - } - return output.PrintDiff(r, diffArgs...) - } - return output.PrintDetails(r) -} - -// showCmd defines the "show" subcommand. -var showCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s show [<option>...] [<commit>]\n\nOptions:\n", arg0) - showFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return showReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/commands/submit.go b/third_party/go/git-appraise/commands/submit.go deleted file mode 100644 index 58fa00235087..000000000000 --- a/third_party/go/git-appraise/commands/submit.go +++ /dev/null @@ -1,157 +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 commands - -import ( - "errors" - "flag" - "fmt" - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review" -) - -var submitFlagSet = flag.NewFlagSet("submit", flag.ExitOnError) - -var ( - submitMerge = submitFlagSet.Bool("merge", false, "Create a merge of the source and target refs.") - submitRebase = submitFlagSet.Bool("rebase", false, "Rebase the source ref onto the target ref.") - submitFastForward = submitFlagSet.Bool("fast-forward", false, "Create a merge using the default fast-forward mode.") - submitTBR = submitFlagSet.Bool("tbr", false, "(To be reviewed) Force the submission of a review that has not been accepted.") - submitArchive = submitFlagSet.Bool("archive", true, "Prevent the original commit from being garbage collected; only affects rebased submits.") - - submitSign = submitFlagSet.Bool("S", false, - "Sign the contents of the submission") -) - -// Submit the current code review request. -// -// The "args" parameter contains all of the command line arguments that followed the subcommand. -func submitReview(repo repository.Repo, args []string) error { - submitFlagSet.Parse(args) - args = submitFlagSet.Args() - - if *submitMerge && *submitRebase { - return errors.New("Only one of --merge or --rebase is allowed.") - } - - var r *review.Review - var err error - if len(args) > 1 { - return errors.New("Only accepting a single review is supported.") - } - if len(args) == 1 { - r, err = review.Get(repo, args[0]) - } else { - r, err = review.GetCurrent(repo) - } - - if err != nil { - return fmt.Errorf("Failed to load the review: %v\n", err) - } - if r == nil { - return errors.New("There is no matching review.") - } - - if r.Submitted { - return errors.New("The review has already been submitted.") - } - - if !*submitTBR && (r.Resolved == nil || !*r.Resolved) { - return errors.New("Not submitting as the review has not yet been accepted.") - } - - target := r.Request.TargetRef - if err := repo.VerifyGitRef(target); err != nil { - return err - } - source, err := r.GetHeadCommit() - if err != nil { - return err - } - - isAncestor, err := repo.IsAncestor(target, source) - if err != nil { - return err - } - if !isAncestor { - return errors.New("Refusing to submit a non-fast-forward review. First merge the target ref.") - } - - if !(*submitRebase || *submitMerge || *submitFastForward) { - submitStrategy, err := repo.GetSubmitStrategy() - if err != nil { - return err - } - if submitStrategy == "merge" && !*submitRebase && !*submitFastForward { - *submitMerge = true - } - if submitStrategy == "rebase" && !*submitMerge && !*submitFastForward { - *submitRebase = true - } - if submitStrategy == "fast-forward" && !*submitRebase && !*submitMerge { - *submitFastForward = true - } - } - - if *submitRebase { - var err error - if *submitSign { - err = r.RebaseAndSign(*submitArchive) - } else { - err = r.Rebase(*submitArchive) - } - if err != nil { - return err - } - - source, err = r.GetHeadCommit() - if err != nil { - return err - } - } - - if err := repo.SwitchToRef(target); err != nil { - return err - } - if *submitMerge { - submitMessage := fmt.Sprintf("Submitting review %.12s", r.Revision) - if *submitSign { - return repo.MergeAndSignRef(source, false, submitMessage, - r.Request.Description) - } else { - return repo.MergeRef(source, false, submitMessage, - r.Request.Description) - } - } else { - if *submitSign { - return repo.MergeAndSignRef(source, true) - } else { - return repo.MergeRef(source, true) - } - } -} - -// submitCmd defines the "submit" subcommand. -var submitCmd = &Command{ - Usage: func(arg0 string) { - fmt.Printf("Usage: %s submit [<option>...] [<review-hash>]\n\nOptions:\n", arg0) - submitFlagSet.PrintDefaults() - }, - RunMethod: func(repo repository.Repo, args []string) error { - return submitReview(repo, args) - }, -} diff --git a/third_party/go/git-appraise/docs/tutorial.md b/third_party/go/git-appraise/docs/tutorial.md deleted file mode 100644 index 6f95bb7e2ff5..000000000000 --- a/third_party/go/git-appraise/docs/tutorial.md +++ /dev/null @@ -1,404 +0,0 @@ -# Getting started with git-appraise - -This file gives an example code-review workflow using git-appraise. It starts -with cloning a repository and goes all the way through to browsing -your submitted commits. - -The git-appraise tool is largely agnostic of what workflow you use, so feel -free to change things to your liking, but this particular workflow should help -you get started. - -## Cloning your repository - -Since you're using a code review tool, we'll assume that you have a URL that -you can push to and pull from in order to collaborate with the rest of your team. - -First we'll create our local clone of the repository: -```shell -git clone ${URL} example-repo -cd example-repo -``` - -If you are starting from an empty repository, then it's a good practice to add a -README file explaining the purpose of the repository: - -```shell -echo '# Example Repository' > README.md -git add README.md -git commit -m 'Added a README file to the repo' -git push -``` - -## Creating our first review - -Generally, reviews in git-appraise are used to decide if the code in one branch -(called the "source") is ready to merge into another branch (called the -"target"). The meaning of each branch and the policies around merging into a -branch vary from team to team, but for this example we'll use a simple practice -called [GitHub Flow](https://guides.github.com/introduction/flow/). - -Specifically, we'll create a new branch for a particular feature, review the -changes to that branch against our master branch, and then delete the feature -branch once we are done. - -### Creating our change - -Create the feature branch: -```shell -git checkout -b ${USER}/getting-started -git push --set-upstream origin ${USER}/getting-started -``` - -... And make some changes to it: -```shell -echo "This is an example repository used for coming up to speed" >> README.md -git commit -a -m "Added an explanation to the README file" -git push -``` - -### Requesting the review - -Up to this point we've only used the regular commands that come with git. Now, -we will use git-appraise to perform a review: - -Request a review: -```shell -git appraise request -``` - -The output of this will be a summary of the newly requested review: -``` -Review requested: -Commit: 1e6eb14c8014593843c5b5f29377585e4ed55304 -Target Ref: refs/heads/master -Review Ref: refs/heads/ojarjur/getting-started -Message: "Added an explanation to the README file" -``` - -Show the details of the current review: -```shell -git appraise show -``` - -``` -[pending] 1e6eb14c8014 - Added an explanation to the README file - "refs/heads/ojarjur/getting-started" -> "refs/heads/master" - reviewers: "" - requester: "ojarjur@google.com" - build status: unknown - analyses: No analyses available - comments (0 threads): -``` - -Show the changes included in the review: -```shell -git appraise show --diff -``` - -``` -diff --git a/README.md b/README.md -index 08fde78..85c4208 100644 ---- a/README.md -+++ b/README.md -@@ -1 +1,2 @@ - # Example Repository -+This is an example repository used for coming up to speed -``` - -### Sending our updates to the remote repository - -Before a teammate can review our change, we have to make it available to them. -This involves pushing both our commits, and our code review data to the remote -repository: -```shell -git push -git appraise pull -git appraise push -``` - -The command `git appraise pull` is used to make sure that our local code review -data includes everything from the remote repo before we try to push our changes -back to it. If you forget to run this command, then the subsequent call to -`git appraise push` might fail with a message that the push was rejected. If -that happens, simply run `git appraise pull` and try again. - -## Reviewing the change - -Your teammates can review your changes using the same tool. - -Fetch the current data from the remote repository: -```shell -git fetch origin -git appraise pull -``` - -List the open reviews: -```shell -git appraise list -``` - -The output of this command will be a list of entries formatted like this: -``` -Loaded 1 open reviews: -[pending] 1e6eb14c8014 - Added an explanation to the README file -``` - -The text within the square brackets is the status of a review, and for open -reviews will be one of "pending", "accepted", or "rejected". The text which -follows the status is the hash of the first commit in the review. This is -used to uniquely identify reviews, and most git-appraise commands will accept -this hash as an argument in order to select the review to handle. - -For instance, we can see the details of a specific review using the "show" -subcommand: -```shell -git appraise show 1e6eb14c8014 -``` - -``` -[pending] 1e6eb14c8014 - Added an explanation to the README file - "refs/heads/ojarjur/getting-started" -> "refs/heads/master" - reviewers: "" - requester: "ojarjur@google.com" - build status: unknown - analyses: No analyses available - comments (0 threads): -``` - -... or, we can see the diff of the changes under review: -```shell -git appraise show --diff 1e6eb14c8014 -``` - -``` -diff --git a/README.md b/README.md -index 08fde78..85c4208 100644 ---- a/README.md -+++ b/README.md -@@ -1 +1,2 @@ - # Example Repository -+This is an example repository used for coming up to speed -``` - -Comments can be added either for the entire review, or on individual lines: -```shell -git appraise comment -f README.md -l 2 -m "Ah, so that's what this is" 1e6eb14c8014 -``` - -These comments then show up in the output of `git appraise show`: -```shell -git appraise show 1e6eb14c8014 -``` - -``` -[pending] 1e6eb14c8014 - Added an explanation to the README file - "refs/heads/ojarjur/getting-started" -> "refs/heads/master" - reviewers: "" - requester: "ojarjur@google.com" - build status: unknown - analyses: No analyses available - comments (1 threads): - "README.md"@1e6eb14c8014 - |# Example Repository - |This is an example repository used for coming up to speed - comment: bd4c11ecafd443c9d1dde6035e89804160cd7487 - author: ojarjur@google.com - time: Fri Dec 18 10:58:54 PST 2015 - status: fyi - Ah, so that's what this is -``` - -Comments initially only exist in your local repository, so to share them -with the rest of your team you have to push your review changes back: - -```shell -git appraise pull -git appraise push -``` - -When the change is ready to be merged, you indicate that by accepting the -review: - -```shell -git appraise accept 1e6eb14c8014 -git appraise pull -git appraise push -``` - -The updated status of the review will be visible in the output of "show": -```shell -git appraise show 1e6eb14c8014 -``` - -``` -[accepted] 1e6eb14c8014 - Added an explanation to the README file - "refs/heads/ojarjur/getting-started" -> "refs/heads/master" - reviewers: "" - requester: "ojarjur@google.com" - build status: unknown - analyses: No analyses available - comments (2 threads): - "README.md"@1e6eb14c8014 - |# Example Repository - |This is an example repository used for coming up to speed - comment: bd4c11ecafd443c9d1dde6035e89804160cd7487 - author: ojarjur@google.com - time: Fri Dec 18 10:58:54 PST 2015 - status: fyi - Ah, so that's what this is - comment: 4034c60e6ed6f24b01e9a581087d1ab86d376b81 - author: ojarjur@google.com - time: Fri Dec 18 11:02:45 PST 2015 - status: fyi -``` - -## Submitting the change - -Once a review has been accepted, you can merge it with the tool: - -```shell -git appraise submit --merge 1e6eb14c8014 -git push -``` - -The submit command will pop up a text editor where you can edit the default -merge message. That message will be used to create a new commit that is a -merge of the previous commit on the master branch, and the history of all -of your changes to the review. You can see what this looks like using -the `git log --graph` command: - -``` -* commit 3a4d1b8cd264b921c858185f2c36aac283b45e49 -|\ Merge: b404fa3 1e6eb14 -| | Author: Omar Jarjur <ojarjur@google.com> -| | Date: Fri Dec 18 11:06:24 2015 -0800 -| | -| | Submitting review 1e6eb14c8014 -| | -| | Added an explanation to the README file -| | -| * commit 1e6eb14c8014593843c5b5f29377585e4ed55304 -|/ Author: Omar Jarjur <ojarjur@google.com> -| Date: Fri Dec 18 10:49:56 2015 -0800 -| -| Added an explanation to the README file -| -* commit b404fa39ae98950d95ab06012191f58507e51d12 - Author: Omar Jarjur <ojarjur@google.com> - Date: Fri Dec 18 10:48:06 2015 -0800 - - Added a README file to the repo -``` - -This is sometimes called a "merge bubble". When the review is simply accepted -as is, these do not add much value. However, reviews often go through several -rounds of changes before they are accepted. By using these merge commits, we -can preserve both the full history of individual reviews, and the high-level -(review-based) history of the repository. - -This can be seen with the history of git-appraise itself. We can see the high -level review history using `git log --first-parent`: - -``` -commit 83c4d770cfde25c943de161c0cac54d714b7de38 -Merge: 9a607b8 931d1b4 -Author: Omar Jarjur <ojarjur@google.com> -Date: Fri Dec 18 09:46:10 2015 -0800 - - Submitting review 8cb887077783 - - Fix a bug where requesting a review would fail with an erroneous message. - - We were figuring out the set of commits to include in a review by - listing the commits between the head of the target ref and the head of - the source ref. However, this only works if the source ref is a - fast-forward of the target ref. - - This commit changes it so that we use the merge-base of the target and - source refs as the starting point instead of the target ref. - -commit 9a607b8529d7483e5b323303c73da05843ff3ca9 -Author: Harry Lawrence <hazbo@gmx.com> -Date: Fri Dec 18 10:24:00 2015 +0000 - - Added links to Eclipse and Jenkins plugins - - As suggested in #11 - -commit 8876cfff2ed848d50cb559c05d44e11b95ca791c -Merge: 00c0e82 1436c83 -Author: Omar Jarjur <ojarjur@google.com> -Date: Thu Dec 17 12:46:32 2015 -0800 - - Submitting review 09aecba64027 - - Force default git editor when omitting -m - For review comments, the absence of the -m flag will now attempt to load the - user's default git editor. - - i.e. git appraise comment c0a643ff39dd - - An initial draft as discussed in #8 - - I'm still not sure whether or not the file that is saved is in the most appropriate place or not. I like the idea of it being relative to the project although it could have gone in `/tmp` I suppose. - -commit 00c0e827e5b86fb9d200f474d4f65f43677cbc6c -Merge: 31209ce 41fde0b -Author: Omar Jarjur <ojarjur@google.com> -Date: Wed Dec 16 17:10:06 2015 -0800 - - Submitting review 2c9bff89f0f8 - - Improve the error messages returned when a git command fails. - - Previously, we were simply cascading the error returned by the instance - of exec.Command. However, that winds up just being something of the form - "exit status 128", with all of the real error message going to the - Stderr field. - - As such, this commit changes the behavior to save the data written to - stderr, and use it to construct a new error to return. - -... -``` - -Here you see a linear view of the reviews that have been submitted, but if we -run the command `git log --oneline --graph`, then we can see that the full -history of each individual review is also available: - -``` -* 83c4d77 Submitting review 8cb887077783 -|\ -| * 931d1b4 Merge branch 'master' into ojarjur/fix-request-bug -| |\ -| |/ -|/| -* | 9a607b8 Added links to Eclipse and Jenkins plugins -| * c7be567 Merge branch 'master' into ojarjur/fix-request-bug -| |\ -| |/ -|/| -* | 8876cff Submitting review 09aecba64027 -|\ \ -| * | 1436c83 Using git var GIT_EDITOR rather than git config -| * | 09aecba Force default git editor when omitting -m -|/ / -| * 8cb8870 Fix a bug where requesting a review would fail with an erroneous message. -|/ -* 00c0e82 Submitting review 2c9bff89f0f8 -... -``` - -## Cleaning up - -Now that our feature branch has been merged into master, we can delete it: - -```shell -git branch -d ${USER}/getting-started -git push origin --delete ${USER}/getting-started -``` diff --git a/third_party/go/git-appraise/git-appraise/default.nix b/third_party/go/git-appraise/git-appraise/default.nix deleted file mode 100644 index 24a06ca95193..000000000000 --- a/third_party/go/git-appraise/git-appraise/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -# This file was generated by https://github.com/kamilchm/go2nix v1.3.0 -{ stdenv, buildGoPackage, fetchgit, fetchhg, fetchbzr, fetchsvn }: - -buildGoPackage rec { - name = "git-appraise-unstable-${version}"; - version = "2019-01-16"; - rev = "2261b194e7ffd6dea6145dac3d0a25e564f8e3fc"; - - goPackagePath = "github.com/google/git-appraise"; - - src = fetchgit { - inherit rev; - url = "https://github.com/google/git-appraise"; - sha256 = "0flvpn1mcmgpjmfmndyx2rnn5n5rb0344590if81x5jz11qj4x0c"; - }; - - goDeps = ./deps.nix; -} diff --git a/third_party/go/git-appraise/git-appraise/deps.nix b/third_party/go/git-appraise/git-appraise/deps.nix deleted file mode 100644 index 0429d8876b29..000000000000 --- a/third_party/go/git-appraise/git-appraise/deps.nix +++ /dev/null @@ -1,3 +0,0 @@ -# This file was generated by https://github.com/kamilchm/go2nix v1.3.0 -[ -] diff --git a/third_party/go/git-appraise/git-appraise/git-appraise.go b/third_party/go/git-appraise/git-appraise/git-appraise.go deleted file mode 100644 index ca5a30cf6da0..000000000000 --- a/third_party/go/git-appraise/git-appraise/git-appraise.go +++ /dev/null @@ -1,104 +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. -*/ - -// Command git-appraise manages code reviews stored as git-notes in the source repo. -// -// To install, run: -// -// $ go get github.com/google/git-appraise/git-appraise -// -// And for usage information, run: -// -// $ git-appraise help -package main - -import ( - "fmt" - "github.com/google/git-appraise/commands" - "github.com/google/git-appraise/repository" - "os" - "sort" - "strings" -) - -const usageMessageTemplate = `Usage: %s <command> - -Where <command> is one of: - %s - -For individual command usage, run: - %s help <command> -` - -func usage() { - command := os.Args[0] - var subcommands []string - for subcommand := range commands.CommandMap { - subcommands = append(subcommands, subcommand) - } - sort.Strings(subcommands) - fmt.Printf(usageMessageTemplate, command, strings.Join(subcommands, "\n "), command) -} - -func help() { - if len(os.Args) < 3 { - usage() - return - } - subcommand, ok := commands.CommandMap[os.Args[2]] - if !ok { - fmt.Printf("Unknown command %q\n", os.Args[2]) - usage() - return - } - subcommand.Usage(os.Args[0]) -} - -func main() { - if len(os.Args) > 1 && os.Args[1] == "help" { - help() - return - } - cwd, err := os.Getwd() - if err != nil { - fmt.Printf("Unable to get the current working directory: %q\n", err) - return - } - repo, err := repository.NewGitRepo(cwd) - if err != nil { - fmt.Printf("%s must be run from within a git repo.\n", os.Args[0]) - return - } - if len(os.Args) < 2 { - subcommand, ok := commands.CommandMap["list"] - if !ok { - fmt.Printf("Unable to list reviews") - return - } - subcommand.Run(repo, []string{}) - return - } - subcommand, ok := commands.CommandMap[os.Args[1]] - if !ok { - fmt.Printf("Unknown command: %q\n", os.Args[1]) - usage() - return - } - if err := subcommand.Run(repo, os.Args[2:]); err != nil { - fmt.Println(err.Error()) - os.Exit(1) - } -} 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) -} diff --git a/third_party/go/git-appraise/review/analyses/analyses.go b/third_party/go/git-appraise/review/analyses/analyses.go deleted file mode 100644 index 4828f3b230c2..000000000000 --- a/third_party/go/git-appraise/review/analyses/analyses.go +++ /dev/null @@ -1,160 +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 analyses defines the internal representation of static analysis reports. -package analyses - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "sort" - "strconv" - - "github.com/google/git-appraise/repository" -) - -const ( - // Ref defines the git-notes ref that we expect to contain analysis reports. - Ref = "refs/notes/devtools/analyses" - - // StatusLooksGoodToMe is the status string representing that analyses reported no messages. - StatusLooksGoodToMe = "lgtm" - // StatusForYourInformation is the status string representing that analyses reported informational messages. - StatusForYourInformation = "fyi" - // StatusNeedsMoreWork is the status string representing that analyses reported error messages. - StatusNeedsMoreWork = "nmw" - - // FormatVersion defines the latest version of the request format supported by the tool. - FormatVersion = 0 -) - -// Report represents a build/test status report generated by analyses tool. -// Every field is optional. -type Report struct { - Timestamp string `json:"timestamp,omitempty"` - URL string `json:"url,omitempty"` - Status string `json:"status,omitempty"` - // Version represents the version of the metadata format. - Version int `json:"v,omitempty"` -} - -// LocationRange represents the location within a source file that an analysis message covers. -type LocationRange struct { - StartLine uint32 `json:"start_line,omitempty"` - StartColumn uint32 `json:"start_column,omitempty"` - EndLine uint32 `json:"end_line,omitempty"` - EndColumn uint32 `json:"end_column,omitempty"` -} - -// Location represents the location within a source tree that an analysis message covers. -type Location struct { - Path string `json:"path,omitempty"` - Range *LocationRange `json:"range,omitempty"` -} - -// Note represents a single analysis message. -type Note struct { - Location *Location `json:"location,omitempty"` - Category string `json:"category,omitempty"` - Description string `json:"description"` -} - -// AnalyzeResponse represents the response from a static-analysis tool. -type AnalyzeResponse struct { - Notes []Note `json:"note,omitempty"` -} - -// ReportDetails represents an entire static analysis run (which might include multiple analysis tools). -type ReportDetails struct { - AnalyzeResponse []AnalyzeResponse `json:"analyze_response,omitempty"` -} - -// GetLintReportResult downloads the details of a lint report and returns the responses embedded in it. -func (analysesReport Report) GetLintReportResult() ([]AnalyzeResponse, error) { - if analysesReport.URL == "" { - return nil, nil - } - res, err := http.Get(analysesReport.URL) - if err != nil { - return nil, err - } - analysesResults, err := ioutil.ReadAll(res.Body) - res.Body.Close() - if err != nil { - return nil, err - } - var details ReportDetails - err = json.Unmarshal([]byte(analysesResults), &details) - if err != nil { - return nil, err - } - return details.AnalyzeResponse, nil -} - -// GetNotes downloads the details of an analyses report and returns the notes embedded in it. -func (analysesReport Report) GetNotes() ([]Note, error) { - reportResults, err := analysesReport.GetLintReportResult() - if err != nil { - return nil, err - } - var reportNotes []Note - for _, reportResult := range reportResults { - reportNotes = append(reportNotes, reportResult.Notes...) - } - return reportNotes, nil -} - -// Parse parses an analysis report from a git note. -func Parse(note repository.Note) (Report, error) { - bytes := []byte(note) - var report Report - err := json.Unmarshal(bytes, &report) - return report, err -} - -// GetLatestAnalysesReport takes a collection of analysis reports, and returns the one with the most recent timestamp. -func GetLatestAnalysesReport(reports []Report) (*Report, error) { - timestampReportMap := make(map[int]*Report) - var timestamps []int - - for _, report := range reports { - timestamp, err := strconv.Atoi(report.Timestamp) - if err != nil { - return nil, err - } - timestamps = append(timestamps, timestamp) - timestampReportMap[timestamp] = &report - } - if len(timestamps) == 0 { - return nil, nil - } - sort.Sort(sort.Reverse(sort.IntSlice(timestamps))) - return timestampReportMap[timestamps[0]], nil -} - -// ParseAllValid takes collection of git notes and tries to parse a analyses report -// from each one. Any notes that are not valid analyses reports get ignored. -func ParseAllValid(notes []repository.Note) []Report { - var reports []Report - for _, note := range notes { - report, err := Parse(note) - if err == nil && report.Version == FormatVersion { - reports = append(reports, report) - } - } - return reports -} diff --git a/third_party/go/git-appraise/review/analyses/analyses_test.go b/third_party/go/git-appraise/review/analyses/analyses_test.go deleted file mode 100644 index 00a811ef6a40..000000000000 --- a/third_party/go/git-appraise/review/analyses/analyses_test.go +++ /dev/null @@ -1,77 +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 analyses - -import ( - "fmt" - "github.com/google/git-appraise/repository" - "net/http" - "net/http/httptest" - "testing" -) - -const ( - mockOldReport = `{"timestamp": "0", "url": "https://this-url-does-not-exist.test/analysis.json"}` - mockNewReport = `{"timestamp": "1", "url": "%s"}` - mockResults = `{ - "analyze_response": [{ - "note": [{ - "location": { - "path": "file.txt", - "range": { - "start_line": 5 - } - }, - "category": "test", - "description": "This is a test" - }] - }] -}` -) - -func mockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - t.Log(r) - fmt.Fprintln(w, mockResults) - w.WriteHeader(http.StatusOK) - } -} - -func TestGetLatestResult(t *testing.T) { - mockServer := httptest.NewServer(http.HandlerFunc(mockHandler(t))) - defer mockServer.Close() - - reports := ParseAllValid([]repository.Note{ - repository.Note([]byte(mockOldReport)), - repository.Note([]byte(fmt.Sprintf(mockNewReport, mockServer.URL))), - }) - - report, err := GetLatestAnalysesReport(reports) - if err != nil { - t.Fatal("Unexpected error while parsing analysis reports", err) - } - if report == nil { - t.Fatal("Unexpected nil report") - } - reportResult, err := report.GetLintReportResult() - if err != nil { - t.Fatal("Unexpected error while reading the latest report's results", err) - } - if len(reportResult) != 1 { - t.Fatal("Unexpected report result", reportResult) - } -} diff --git a/third_party/go/git-appraise/review/ci/ci.go b/third_party/go/git-appraise/review/ci/ci.go deleted file mode 100644 index b2cfd22743c2..000000000000 --- a/third_party/go/git-appraise/review/ci/ci.go +++ /dev/null @@ -1,95 +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 ci defines the internal representation of a continuous integration reports. -package ci - -import ( - "encoding/json" - "github.com/google/git-appraise/repository" - "sort" - "strconv" -) - -const ( - // Ref defines the git-notes ref that we expect to contain CI reports. - Ref = "refs/notes/devtools/ci" - - // StatusSuccess is the status string representing that a build and/or test passed. - StatusSuccess = "success" - // StatusFailure is the status string representing that a build and/or test failed. - StatusFailure = "failure" - - // FormatVersion defines the latest version of the request format supported by the tool. - FormatVersion = 0 -) - -// Report represents a build/test status report generated by a continuous integration tool. -// -// Every field is optional. -type Report struct { - Timestamp string `json:"timestamp,omitempty"` - URL string `json:"url,omitempty"` - Status string `json:"status,omitempty"` - Agent string `json:"agent,omitempty"` - // Version represents the version of the metadata format. - Version int `json:"v,omitempty"` -} - -// Parse parses a CI report from a git note. -func Parse(note repository.Note) (Report, error) { - bytes := []byte(note) - var report Report - err := json.Unmarshal(bytes, &report) - return report, err -} - -// GetLatestCIReport takes the collection of reports and returns the one with the most recent timestamp. -func GetLatestCIReport(reports []Report) (*Report, error) { - timestampReportMap := make(map[int]*Report) - var timestamps []int - - for _, report := range reports { - timestamp, err := strconv.Atoi(report.Timestamp) - if err != nil { - return nil, err - } - timestamps = append(timestamps, timestamp) - timestampReportMap[timestamp] = &report - } - if len(timestamps) == 0 { - return nil, nil - } - sort.Sort(sort.Reverse(sort.IntSlice(timestamps))) - return timestampReportMap[timestamps[0]], nil -} - -// ParseAllValid takes collection of git notes and tries to parse a CI report -// from each one. Any notes that are not valid CI reports get ignored, as we -// expect the git notes to be a heterogenous list, with only some of them -// being valid CI status reports. -func ParseAllValid(notes []repository.Note) []Report { - var reports []Report - for _, note := range notes { - report, err := Parse(note) - if err == nil && report.Version == FormatVersion { - if report.Status == "" || report.Status == StatusSuccess || report.Status == StatusFailure { - reports = append(reports, report) - } - } - } - return reports -} diff --git a/third_party/go/git-appraise/review/ci/ci_test.go b/third_party/go/git-appraise/review/ci/ci_test.go deleted file mode 100644 index c141f053d94d..000000000000 --- a/third_party/go/git-appraise/review/ci/ci_test.go +++ /dev/null @@ -1,85 +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 ci - -import ( - "github.com/google/git-appraise/repository" - "testing" -) - -const testCINote1 = `{ - "Timestamp": "4", - "URL": "www.google.com", - "Status": "success" -}` - -const testCINote2 = `{ - "Timestamp": "16", - "URL": "www.google.com", - "Status": "failure" -}` - -const testCINote3 = `{ - "Timestamp": "30", - "URL": "www.google.com", - "Status": "something else" -}` - -const testCINote4 = `{ - "Timestamp": "28", - "URL": "www.google.com", - "Status": "success" -}` - -const testCINote5 = `{ - "Timestamp": "27", - "URL": "www.google.com", - "Status": "success" -}` - -func TestCIReport(t *testing.T) { - latestReport, err := GetLatestCIReport(ParseAllValid([]repository.Note{ - repository.Note(testCINote1), - repository.Note(testCINote2), - })) - if err != nil { - t.Fatal("Failed to properly fetch the latest report", err) - } - expected, err := Parse(repository.Note(testCINote2)) - if err != nil { - t.Fatal("Failed to parse the expected report", err) - } - if *latestReport != expected { - t.Fatal("This is not the latest ", latestReport) - } - latestReport, err = GetLatestCIReport(ParseAllValid([]repository.Note{ - repository.Note(testCINote1), - repository.Note(testCINote2), - repository.Note(testCINote3), - repository.Note(testCINote4), - })) - if err != nil { - t.Fatal("Failed to properly fetch the latest report", err) - } - expected, err = Parse(repository.Note(testCINote4)) - if err != nil { - t.Fatal("Failed to parse the expected report", err) - } - if *latestReport != expected { - t.Fatal("This is not the latest ", latestReport) - } -} diff --git a/third_party/go/git-appraise/review/comment/comment.go b/third_party/go/git-appraise/review/comment/comment.go deleted file mode 100644 index b1dea49c13e4..000000000000 --- a/third_party/go/git-appraise/review/comment/comment.go +++ /dev/null @@ -1,266 +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 comment defines the internal representation of a review comment. -package comment - -import ( - "crypto/sha1" - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - "time" - - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review/gpg" -) - -// Ref defines the git-notes ref that we expect to contain review comments. -const Ref = "refs/notes/devtools/discuss" - -// FormatVersion defines the latest version of the comment format supported by the tool. -const FormatVersion = 0 - -// ErrInvalidRange inidcates an error during parsing of a user-defined file -// range -var ErrInvalidRange = errors.New("invalid file location range. The required form is StartLine[+StartColumn][:EndLine[+EndColumn]]. The first line in a file is considered to be line 1") - -// Range represents the range of text that is under discussion. -type Range struct { - StartLine uint32 `json:"startLine"` - StartColumn uint32 `json:"startColumn,omitempty"` - EndLine uint32 `json:"endLine,omitempty"` - EndColumn uint32 `json:"endColumn,omitempty"` -} - -// Location represents the location of a comment within a commit. -type Location struct { - Commit string `json:"commit,omitempty"` - // If the path is omitted, then the comment applies to the entire commit. - Path string `json:"path,omitempty"` - // If the range is omitted, then the location represents an entire file. - Range *Range `json:"range,omitempty"` -} - -// Check verifies that this location is valid in the provided -// repository. -func (location *Location) Check(repo repository.Repo) error { - contents, err := repo.Show(location.Commit, location.Path) - if err != nil { - return err - } - lines := strings.Split(contents, "\n") - if location.Range.StartLine > uint32(len(lines)) { - return fmt.Errorf("Line number %d does not exist in file %q", - location.Range.StartLine, - location.Path) - } - if location.Range.StartColumn != 0 && - location.Range.StartColumn > uint32(len(lines[location.Range.StartLine-1])) { - return fmt.Errorf("Line %d in %q is too short for column %d", - location.Range.StartLine, - location.Path, - location.Range.StartColumn) - } - if location.Range.EndLine != 0 && - location.Range.EndLine > uint32(len(lines)) { - return fmt.Errorf("End line number %d does not exist in file %q", - location.Range.EndLine, - location.Path) - } - if location.Range.EndColumn != 0 && - location.Range.EndColumn > uint32(len(lines[location.Range.EndLine-1])) { - return fmt.Errorf("End line %d in %q is too short for column %d", - location.Range.EndLine, - location.Path, - location.Range.EndColumn) - } - return nil -} - -// Comment represents a review comment, and can occur in any of the following contexts: -// 1. As a comment on an entire commit. -// 2. As a comment about a specific file in a commit. -// 3. As a comment about a specific line in a commit. -// 4. As a response to another comment. -type Comment struct { - // Timestamp and Author are optimizations that allows us to display comment threads - // without having to run git-blame over the notes object. This is done because - // git-blame will become more and more expensive as the number of code reviews grows. - Timestamp string `json:"timestamp,omitempty"` - Author string `json:"author,omitempty"` - // If original is provided, then the comment is an updated version of another comment. - Original string `json:"original,omitempty"` - // If parent is provided, then the comment is a response to another comment. - Parent string `json:"parent,omitempty"` - // If location is provided, then the comment is specific to that given location. - Location *Location `json:"location,omitempty"` - Description string `json:"description,omitempty"` - // The resolved bit indicates that no further action is needed. - // - // When the parent of the comment is another comment, this means that comment - // has been addressed. Otherwise, the parent is the commit, and this means that the - // change has been accepted. If the resolved bit is unset, then the comment is only an FYI. - Resolved *bool `json:"resolved,omitempty"` - // Version represents the version of the metadata format. - Version int `json:"v,omitempty"` - - gpg.Sig -} - -// New returns a new comment with the given description message. -// -// The Timestamp and Author fields are automatically filled in with the current time and user. -func New(author string, description string) Comment { - return Comment{ - Timestamp: strconv.FormatInt(time.Now().Unix(), 10), - Author: author, - Description: description, - } -} - -// Parse parses a review comment from a git note. -func Parse(note repository.Note) (Comment, error) { - bytes := []byte(note) - var comment Comment - err := json.Unmarshal(bytes, &comment) - return comment, err -} - -// ParseAllValid takes collection of git notes and tries to parse a review -// comment from each one. Any notes that are not valid review comments get -// ignored, as we expect the git notes to be a heterogenous list, with only -// some of them being review comments. -func ParseAllValid(notes []repository.Note) map[string]Comment { - comments := make(map[string]Comment) - for _, note := range notes { - comment, err := Parse(note) - if err == nil && comment.Version == FormatVersion { - hash, err := comment.Hash() - if err == nil { - comments[hash] = comment - } - } - } - return comments -} - -func (comment Comment) serialize() ([]byte, error) { - if len(comment.Timestamp) < 10 { - // To make sure that timestamps from before 2001 appear in the correct - // alphabetical order, we reformat the timestamp to be at least 10 characters - // and zero-padded. - time, err := strconv.ParseInt(comment.Timestamp, 10, 64) - if err == nil { - comment.Timestamp = fmt.Sprintf("%010d", time) - } - // We ignore the other case, as the comment timestamp is not in a format - // we expected, so we should just leave it alone. - } - return json.Marshal(comment) -} - -// Write writes a review comment as a JSON-formatted git note. -func (comment Comment) Write() (repository.Note, error) { - bytes, err := comment.serialize() - return repository.Note(bytes), err -} - -// Hash returns the SHA1 hash of a review comment. -func (comment Comment) Hash() (string, error) { - bytes, err := comment.serialize() - return fmt.Sprintf("%x", sha1.Sum(bytes)), err -} - -// Set implenents flag.Value for the Range type -func (r *Range) Set(s string) error { - var err error - *r = Range{} - - if s == "" { - return nil - } - startEndParts := strings.Split(s, ":") - if len(startEndParts) > 2 { - return ErrInvalidRange - } - - r.StartLine, r.StartColumn, err = parseRangePart(startEndParts[0]) - if err != nil { - return err - } - if len(startEndParts) == 1 { - return nil - } - - r.EndLine, r.EndColumn, err = parseRangePart(startEndParts[1]) - if err != nil { - return err - } - - if r.StartLine > r.EndLine { - return errors.New("start line cannot be greater than end line in range") - } - - return nil -} - -func parseRangePart(s string) (uint32, uint32, error) { - parts := strings.Split(s, "+") - if len(parts) > 2 { - return 0, 0, ErrInvalidRange - } - - line, err := strconv.ParseUint(parts[0], 10, 32) - if err != nil { - return 0, 0, ErrInvalidRange - } - - if len(parts) == 1 { - return uint32(line), 0, nil - } - - col, err := strconv.ParseUint(parts[1], 10, 32) - if err != nil { - return 0, 0, ErrInvalidRange - } - - if line == 0 && col != 0 { - // line 0 represents the entire file - return 0, 0, ErrInvalidRange - } - - return uint32(line), uint32(col), nil -} - -func (r *Range) String() string { - out := "" - if r.StartLine != 0 { - out = fmt.Sprintf("%d", r.StartLine) - } - if r.StartColumn != 0 { - out = fmt.Sprintf("%s+%d", out, r.StartColumn) - } - if r.EndLine != 0 { - out = fmt.Sprintf("%s:%d", out, r.EndLine) - } - if r.EndColumn != 0 { - out = fmt.Sprintf("%s+%d", out, r.EndColumn) - } - return out -} diff --git a/third_party/go/git-appraise/review/gpg/signable.go b/third_party/go/git-appraise/review/gpg/signable.go deleted file mode 100644 index 776764c6fc10..000000000000 --- a/third_party/go/git-appraise/review/gpg/signable.go +++ /dev/null @@ -1,129 +0,0 @@ -// Package gpg provides an interface and an abstraction with which to sign and -// verify review requests and comments. -package gpg - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "os/exec" -) - -const placeholder = "gpgsig" - -// Sig provides an abstraction around shelling out to GPG to sign the -// content it's given. -type Sig struct { - // Sig holds an object's content's signature. - Sig string `json:"signature,omitempty"` -} - -// Signable is an interfaces which provides the pointer to the signable -// object's stringified signature. -// -// This pointer is used by `Sign` and `Verify` to replace its contents with -// `placeholder` or the signature itself for the purposes of signing or -// verifying. -type Signable interface { - Signature() *string -} - -// Signature is `Sig`'s implementation of `Signable`. Through this function, an -// object which needs to implement `Signable` need only embed `Sig` -// anonymously. See, e.g., review/request.go. -func (s *Sig) Signature() *string { - return &s.Sig -} - -// Sign uses gpg to sign the contents of a request and deposit it into the -// signature key of the request. -func Sign(key string, s Signable) error { - // First we retrieve the pointer and write `placeholder` as its value. - sigPtr := s.Signature() - *sigPtr = placeholder - - // Marshal the content and sign it. - content, err := json.Marshal(s) - if err != nil { - return err - } - sig, err := signContent(key, content) - if err != nil { - return err - } - - // Write the signature as the new value at the pointer. - *sigPtr = sig.String() - return nil -} - -func signContent(key string, content []byte) (*bytes.Buffer, - error) { - var stdout, stderr bytes.Buffer - cmd := exec.Command("gpg", "-u", key, "--detach-sign", "--armor") - cmd.Stdin = bytes.NewReader(content) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - return &stdout, err -} - -// Verify verifies the signatures on the request and its comments with the -// given key. -func Verify(s Signable) error { - // Retrieve the pointer. - sigPtr := s.Signature() - // Copy its contents. - sig := *sigPtr - // Overwrite the value with the placeholder. - *sigPtr = placeholder - - defer func() { *sigPtr = sig }() - - // 1. Marshal the content into JSON. - // 2. Write the signature and the content to temp files. - // 3. Use gpg to verify the signature. - content, err := json.Marshal(s) - if err != nil { - return err - } - sigFile, err := ioutil.TempFile("", "sig") - if err != nil { - return err - } - defer os.Remove(sigFile.Name()) - _, err = sigFile.Write([]byte(sig)) - if err != nil { - return err - } - err = sigFile.Close() - if err != nil { - return err - } - - contentFile, err := ioutil.TempFile("", "content") - if err != nil { - return err - } - defer os.Remove(contentFile.Name()) - _, err = contentFile.Write(content) - if err != nil { - return err - } - err = contentFile.Close() - if err != nil { - return err - } - - var stdout, stderr bytes.Buffer - cmd := exec.Command("gpg", "--verify", sigFile.Name(), contentFile.Name()) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err = cmd.Run() - if err != nil { - return fmt.Errorf("%s", stderr.String()) - } - return nil -} diff --git a/third_party/go/git-appraise/review/request/request.go b/third_party/go/git-appraise/review/request/request.go deleted file mode 100644 index c23fd427a8ee..000000000000 --- a/third_party/go/git-appraise/review/request/request.go +++ /dev/null @@ -1,104 +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 request defines the internal representation of a review request. -package request - -import ( - "encoding/json" - "strconv" - "time" - - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review/gpg" -) - -// Ref defines the git-notes ref that we expect to contain review requests. -const Ref = "refs/notes/devtools/reviews" - -// FormatVersion defines the latest version of the request format supported by the tool. -const FormatVersion = 0 - -// Request represents an initial request for a code review. -// -// Every field is optional. -type Request struct { - // Timestamp and Requester are optimizations that allows us to display reviews - // without having to run git-blame over the notes object. This is done because - // git-blame will become more and more expensive as the number of reviews grows. - Timestamp string `json:"timestamp,omitempty"` - ReviewRef string `json:"reviewRef,omitempty"` - TargetRef string `json:"targetRef"` - Requester string `json:"requester,omitempty"` - Reviewers []string `json:"reviewers,omitempty"` - Description string `json:"description,omitempty"` - // Version represents the version of the metadata format. - Version int `json:"v,omitempty"` - // BaseCommit stores the commit ID of the target ref at the time the review was requested. - // This is optional, and only used for submitted reviews which were anchored at a merge commit. - // This allows someone viewing that submitted review to find the diff against which the - // code was reviewed. - BaseCommit string `json:"baseCommit,omitempty"` - // Alias stores a post-rebase commit ID for the review. This allows the tool - // to track the history of a review even if the commit history changes. - Alias string `json:"alias,omitempty"` - - gpg.Sig -} - -// New returns a new request. -// -// The Timestamp and Requester fields are automatically filled in with the current time and user. -func New(requester string, reviewers []string, reviewRef, targetRef, description string) Request { - return Request{ - Timestamp: strconv.FormatInt(time.Now().Unix(), 10), - Requester: requester, - Reviewers: reviewers, - ReviewRef: reviewRef, - TargetRef: targetRef, - Description: description, - } -} - -// Parse parses a review request from a git note. -func Parse(note repository.Note) (Request, error) { - bytes := []byte(note) - var request Request - err := json.Unmarshal(bytes, &request) - // TODO(ojarjur): If "requester" is not set, then use git-blame to fill it in. - return request, err -} - -// ParseAllValid takes collection of git notes and tries to parse a review -// request from each one. Any notes that are not valid review requests get -// ignored, as we expect the git notes to be a heterogenous list, with only -// some of them being review requests. -func ParseAllValid(notes []repository.Note) []Request { - var requests []Request - for _, note := range notes { - request, err := Parse(note) - if err == nil && request.Version == FormatVersion { - requests = append(requests, request) - } - } - return requests -} - -// Write writes a review request as a JSON-formatted git note. -func (request *Request) Write() (repository.Note, error) { - bytes, err := json.Marshal(request) - return repository.Note(bytes), err -} diff --git a/third_party/go/git-appraise/review/review.go b/third_party/go/git-appraise/review/review.go deleted file mode 100644 index a23dd17bf798..000000000000 --- a/third_party/go/git-appraise/review/review.go +++ /dev/null @@ -1,772 +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 review contains the data structures used to represent code reviews. -package review - -import ( - "bytes" - "encoding/json" - "fmt" - "sort" - - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review/analyses" - "github.com/google/git-appraise/review/ci" - "github.com/google/git-appraise/review/comment" - "github.com/google/git-appraise/review/gpg" - "github.com/google/git-appraise/review/request" -) - -const archiveRef = "refs/devtools/archives/reviews" - -// CommentThread represents the tree-based hierarchy of comments. -// -// The Resolved field represents the aggregate status of the entire thread. If -// it is set to false, then it indicates that there is an unaddressed comment -// in the thread. If it is unset, then that means that the root comment is an -// FYI only, and that there are no unaddressed comments. If it is set to true, -// then that means that there are no unaddressed comments, and that the root -// comment has its resolved bit set to true. -type CommentThread struct { - Hash string `json:"hash,omitempty"` - Comment comment.Comment `json:"comment"` - Original *comment.Comment `json:"original,omitempty"` - Edits []*comment.Comment `json:"edits,omitempty"` - Children []CommentThread `json:"children,omitempty"` - Resolved *bool `json:"resolved,omitempty"` - Edited bool `json:"edited,omitempty"` -} - -// Summary represents the high-level state of a code review. -// -// This high-level state corresponds to the data that can be quickly read -// directly from the repo, so other methods that need to operate on a lot -// of reviews (such as listing the open reviews) should prefer operating on -// the summary rather than the details. -// -// Review summaries have two status fields which are orthogonal: -// 1. Resolved indicates if a reviewer has accepted or rejected the change. -// 2. Submitted indicates if the change has been incorporated into the target. -type Summary struct { - Repo repository.Repo `json:"-"` - Revision string `json:"revision"` - Request request.Request `json:"request"` - AllRequests []request.Request `json:"-"` - Comments []CommentThread `json:"comments,omitempty"` - Resolved *bool `json:"resolved,omitempty"` - Submitted bool `json:"submitted"` -} - -// Review represents the entire state of a code review. -// -// This extends Summary to also include a list of reports for both the -// continuous integration status, and the static analysis runs. Those reports -// correspond to either the current commit in the review ref (for pending -// reviews), or to the last commented-upon commit (for submitted reviews). -type Review struct { - *Summary - Reports []ci.Report `json:"reports,omitempty"` - Analyses []analyses.Report `json:"analyses,omitempty"` -} - -type commentsByTimestamp []*comment.Comment - -// Interface methods for sorting comment threads by timestamp -func (cs commentsByTimestamp) Len() int { return len(cs) } -func (cs commentsByTimestamp) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] } -func (cs commentsByTimestamp) Less(i, j int) bool { - return cs[i].Timestamp < cs[j].Timestamp -} - -type byTimestamp []CommentThread - -// Interface methods for sorting comment threads by timestamp -func (threads byTimestamp) Len() int { return len(threads) } -func (threads byTimestamp) Swap(i, j int) { threads[i], threads[j] = threads[j], threads[i] } -func (threads byTimestamp) Less(i, j int) bool { - return threads[i].Comment.Timestamp < threads[j].Comment.Timestamp -} - -type requestsByTimestamp []request.Request - -// Interface methods for sorting review requests by timestamp -func (requests requestsByTimestamp) Len() int { return len(requests) } -func (requests requestsByTimestamp) Swap(i, j int) { - requests[i], requests[j] = requests[j], requests[i] -} -func (requests requestsByTimestamp) Less(i, j int) bool { - return requests[i].Timestamp < requests[j].Timestamp -} - -type summariesWithNewestRequestsFirst []Summary - -// Interface methods for sorting review summaries in reverse chronological order -func (summaries summariesWithNewestRequestsFirst) Len() int { return len(summaries) } -func (summaries summariesWithNewestRequestsFirst) Swap(i, j int) { - summaries[i], summaries[j] = summaries[j], summaries[i] -} -func (summaries summariesWithNewestRequestsFirst) Less(i, j int) bool { - return summaries[i].Request.Timestamp > summaries[j].Request.Timestamp -} - -// updateThreadsStatus calculates the aggregate status of a sequence of comment threads. -// -// The aggregate status is the conjunction of all of the non-nil child statuses. -// -// This has the side-effect of setting the "Resolved" field of all descendant comment threads. -func updateThreadsStatus(threads []CommentThread) *bool { - sort.Stable(byTimestamp(threads)) - noUnresolved := true - var result *bool - for i := range threads { - thread := &threads[i] - thread.updateResolvedStatus() - if thread.Resolved != nil { - noUnresolved = noUnresolved && *thread.Resolved - result = &noUnresolved - } - } - return result -} - -// updateResolvedStatus calculates the aggregate status of a single comment thread, -// and updates the "Resolved" field of that thread accordingly. -func (thread *CommentThread) updateResolvedStatus() { - resolved := updateThreadsStatus(thread.Children) - if resolved == nil { - thread.Resolved = thread.Comment.Resolved - return - } - - if !*resolved { - thread.Resolved = resolved - return - } - - if thread.Comment.Resolved == nil || !*thread.Comment.Resolved { - thread.Resolved = nil - return - } - - thread.Resolved = resolved -} - -// Verify verifies the signature on a comment. -func (thread *CommentThread) Verify() error { - err := gpg.Verify(&thread.Comment) - if err != nil { - hash, _ := thread.Comment.Hash() - return fmt.Errorf("verification of comment [%s] failed: %s", hash, err) - } - for _, child := range thread.Children { - err = child.Verify() - if err != nil { - return err - } - } - return nil -} - -// mutableThread is an internal-only data structure used to store partially constructed comment threads. -type mutableThread struct { - Hash string - Comment comment.Comment - Edits []*comment.Comment - Children []*mutableThread -} - -// fixMutableThread is a helper method to finalize a mutableThread struct -// (partially constructed comment thread) as a CommentThread struct -// (fully constructed comment thread). -func fixMutableThread(mutableThread *mutableThread) CommentThread { - var children []CommentThread - edited := len(mutableThread.Edits) > 0 - for _, mutableChild := range mutableThread.Children { - child := fixMutableThread(mutableChild) - if (!edited) && child.Edited { - edited = true - } - children = append(children, child) - } - comment := &mutableThread.Comment - if len(mutableThread.Edits) > 0 { - sort.Stable(commentsByTimestamp(mutableThread.Edits)) - comment = mutableThread.Edits[len(mutableThread.Edits)-1] - } - - return CommentThread{ - Hash: mutableThread.Hash, - Comment: *comment, - Original: &mutableThread.Comment, - Edits: mutableThread.Edits, - Children: children, - Edited: edited, - } -} - -// This function builds the comment thread tree from the log-based list of comments. -// -// Since the comments can be processed in any order, this uses an internal mutable -// data structure, and then converts it to the proper CommentThread structure at the end. -func buildCommentThreads(commentsByHash map[string]comment.Comment) []CommentThread { - threadsByHash := make(map[string]*mutableThread) - for hash, comment := range commentsByHash { - thread, ok := threadsByHash[hash] - if !ok { - thread = &mutableThread{ - Hash: hash, - Comment: comment, - } - threadsByHash[hash] = thread - } - } - var rootHashes []string - for hash, thread := range threadsByHash { - if thread.Comment.Original != "" { - original, ok := threadsByHash[thread.Comment.Original] - if ok { - original.Edits = append(original.Edits, &thread.Comment) - } - } else if thread.Comment.Parent == "" { - rootHashes = append(rootHashes, hash) - } else { - parent, ok := threadsByHash[thread.Comment.Parent] - if ok { - parent.Children = append(parent.Children, thread) - } - } - } - var threads []CommentThread - for _, hash := range rootHashes { - threads = append(threads, fixMutableThread(threadsByHash[hash])) - } - return threads -} - -// loadComments reads in the log-structured sequence of comments for a review, -// and then builds the corresponding tree-structured comment threads. -func (r *Summary) loadComments(commentNotes []repository.Note) []CommentThread { - commentsByHash := comment.ParseAllValid(commentNotes) - return buildCommentThreads(commentsByHash) -} - -func getSummaryFromNotes(repo repository.Repo, revision string, requestNotes, commentNotes []repository.Note) (*Summary, error) { - requests := request.ParseAllValid(requestNotes) - if requests == nil { - return nil, fmt.Errorf("Could not find any review requests for %q", revision) - } - sort.Stable(requestsByTimestamp(requests)) - reviewSummary := Summary{ - Repo: repo, - Revision: revision, - Request: requests[len(requests)-1], - AllRequests: requests, - } - reviewSummary.Comments = reviewSummary.loadComments(commentNotes) - reviewSummary.Resolved = updateThreadsStatus(reviewSummary.Comments) - return &reviewSummary, nil -} - -// GetSummary returns the summary of the code review specified by its revision -// and the references which contain that reviews summary and comments. -// -// If no review request exists, the returned review summary is nil. -func GetSummaryViaRefs(repo repository.Repo, requestRef, commentRef, - revision string) (*Summary, error) { - - if err := repo.VerifyCommit(revision); err != nil { - return nil, fmt.Errorf("Could not find a commit named %q", revision) - } - requestNotes := repo.GetNotes(requestRef, revision) - commentNotes := repo.GetNotes(commentRef, revision) - summary, err := getSummaryFromNotes(repo, revision, requestNotes, commentNotes) - if err != nil { - return nil, err - } - currentCommit := revision - if summary.Request.Alias != "" { - currentCommit = summary.Request.Alias - } - - if !summary.IsAbandoned() { - submitted, err := repo.IsAncestor(currentCommit, summary.Request.TargetRef) - if err != nil { - return nil, err - } - summary.Submitted = submitted - } - return summary, nil -} - -// GetSummary returns the summary of the specified code review. -// -// If no review request exists, the returned review summary is nil. -func GetSummary(repo repository.Repo, revision string) (*Summary, error) { - return GetSummaryViaRefs(repo, request.Ref, comment.Ref, revision) -} - -// Details returns the detailed review for the given summary. -func (r *Summary) Details() (*Review, error) { - review := Review{ - Summary: r, - } - currentCommit, err := review.GetHeadCommit() - if err == nil { - review.Reports = ci.ParseAllValid(review.Repo.GetNotes(ci.Ref, currentCommit)) - review.Analyses = analyses.ParseAllValid(review.Repo.GetNotes(analyses.Ref, currentCommit)) - } - return &review, nil -} - -// IsAbandoned returns whether or not the given review has been abandoned. -func (r *Summary) IsAbandoned() bool { - return r.Request.TargetRef == "" -} - -// IsOpen returns whether or not the given review is still open (neither submitted nor abandoned). -func (r *Summary) IsOpen() bool { - return !r.Submitted && !r.IsAbandoned() -} - -// Verify returns whether or not a summary's comments are a) signed, and b) -/// that those signatures are verifiable. -func (r *Summary) Verify() error { - err := gpg.Verify(&r.Request) - if err != nil { - return fmt.Errorf("couldn't verify request targeting: %q: %s", - r.Request.TargetRef, err) - } - for _, thread := range r.Comments { - err := thread.Verify() - if err != nil { - return err - } - } - return nil -} - -// Get returns the specified code review. -// -// If no review request exists, the returned review is nil. -func Get(repo repository.Repo, revision string) (*Review, error) { - summary, err := GetSummary(repo, revision) - if err != nil { - return nil, err - } - if summary == nil { - return nil, nil - } - return summary.Details() -} - -func getIsSubmittedCheck(repo repository.Repo) func(ref, commit string) bool { - refCommitsMap := make(map[string]map[string]bool) - - getRefCommitsMap := func(ref string) map[string]bool { - commitsMap, ok := refCommitsMap[ref] - if ok { - return commitsMap - } - commitsMap = make(map[string]bool) - for _, commit := range repo.ListCommits(ref) { - commitsMap[commit] = true - } - refCommitsMap[ref] = commitsMap - return commitsMap - } - - return func(ref, commit string) bool { - return getRefCommitsMap(ref)[commit] - } -} - -func unsortedListAll(repo repository.Repo) []Summary { - reviewNotesMap, err := repo.GetAllNotes(request.Ref) - if err != nil { - return nil - } - discussNotesMap, err := repo.GetAllNotes(comment.Ref) - if err != nil { - return nil - } - - isSubmittedCheck := getIsSubmittedCheck(repo) - var reviews []Summary - for commit, notes := range reviewNotesMap { - summary, err := getSummaryFromNotes(repo, commit, notes, discussNotesMap[commit]) - if err != nil { - continue - } - if !summary.IsAbandoned() { - summary.Submitted = isSubmittedCheck(summary.Request.TargetRef, summary.getStartingCommit()) - } - reviews = append(reviews, *summary) - } - return reviews -} - -// ListAll returns all reviews stored in the git-notes. -func ListAll(repo repository.Repo) []Summary { - reviews := unsortedListAll(repo) - sort.Stable(summariesWithNewestRequestsFirst(reviews)) - return reviews -} - -// ListOpen returns all reviews that are not yet incorporated into their target refs. -func ListOpen(repo repository.Repo) []Summary { - var openReviews []Summary - for _, review := range unsortedListAll(repo) { - if review.IsOpen() { - openReviews = append(openReviews, review) - } - } - sort.Stable(summariesWithNewestRequestsFirst(openReviews)) - return openReviews -} - -// GetCurrent returns the current, open code review. -// -// If there are multiple matching reviews, then an error is returned. -func GetCurrent(repo repository.Repo) (*Review, error) { - reviewRef, err := repo.GetHeadRef() - if err != nil { - return nil, err - } - var matchingReviews []Summary - for _, review := range ListOpen(repo) { - if review.Request.ReviewRef == reviewRef { - matchingReviews = append(matchingReviews, review) - } - } - if matchingReviews == nil { - return nil, nil - } - if len(matchingReviews) != 1 { - return nil, fmt.Errorf("There are %d open reviews for the ref \"%s\"", len(matchingReviews), reviewRef) - } - return matchingReviews[0].Details() -} - -// GetBuildStatusMessage returns a string of the current build-and-test status -// of the review, or "unknown" if the build-and-test status cannot be determined. -func (r *Review) GetBuildStatusMessage() string { - statusMessage := "unknown" - ciReport, err := ci.GetLatestCIReport(r.Reports) - if err != nil { - return fmt.Sprintf("unknown: %s", err) - } - if ciReport != nil { - statusMessage = fmt.Sprintf("%s (%q)", ciReport.Status, ciReport.URL) - } - return statusMessage -} - -// GetAnalysesNotes returns all of the notes from the most recent static -// analysis run recorded in the git notes. -func (r *Review) GetAnalysesNotes() ([]analyses.Note, error) { - latestAnalyses, err := analyses.GetLatestAnalysesReport(r.Analyses) - if err != nil { - return nil, err - } - if latestAnalyses == nil { - return nil, fmt.Errorf("No analyses available") - } - return latestAnalyses.GetNotes() -} - -// GetAnalysesMessage returns a string summarizing the results of the -// most recent static analyses. -func (r *Review) GetAnalysesMessage() string { - latestAnalyses, err := analyses.GetLatestAnalysesReport(r.Analyses) - if err != nil { - return err.Error() - } - if latestAnalyses == nil { - return "No analyses available" - } - status := latestAnalyses.Status - if status != "" && status != analyses.StatusNeedsMoreWork { - return status - } - analysesNotes, err := latestAnalyses.GetNotes() - if err != nil { - return err.Error() - } - if analysesNotes == nil { - return "passed" - } - return fmt.Sprintf("%d warnings\n", len(analysesNotes)) - // TODO(ojarjur): Figure out the best place to display the actual notes -} - -func prettyPrintJSON(jsonBytes []byte) (string, error) { - var prettyBytes bytes.Buffer - err := json.Indent(&prettyBytes, jsonBytes, "", " ") - if err != nil { - return "", err - } - return prettyBytes.String(), nil -} - -// GetJSON returns the pretty printed JSON for a review summary. -func (r *Summary) GetJSON() (string, error) { - jsonBytes, err := json.Marshal(*r) - if err != nil { - return "", err - } - return prettyPrintJSON(jsonBytes) -} - -// GetJSON returns the pretty printed JSON for a review. -func (r *Review) GetJSON() (string, error) { - jsonBytes, err := json.Marshal(*r) - if err != nil { - return "", err - } - return prettyPrintJSON(jsonBytes) -} - -// findLastCommit returns the later (newest) commit from the union of the provided commit -// and all of the commits that are referenced in the given comment threads. -func (r *Review) findLastCommit(startingCommit, latestCommit string, commentThreads []CommentThread) string { - isLater := func(commit string) bool { - if err := r.Repo.VerifyCommit(commit); err != nil { - return false - } - if t, e := r.Repo.IsAncestor(latestCommit, commit); e == nil && t { - return true - } - if t, e := r.Repo.IsAncestor(startingCommit, commit); e == nil && !t { - return false - } - if t, e := r.Repo.IsAncestor(commit, latestCommit); e == nil && t { - return false - } - ct, err := r.Repo.GetCommitTime(commit) - if err != nil { - return false - } - lt, err := r.Repo.GetCommitTime(latestCommit) - if err != nil { - return true - } - return ct > lt - } - updateLatest := func(commit string) { - if commit == "" { - return - } - if isLater(commit) { - latestCommit = commit - } - } - for _, commentThread := range commentThreads { - comment := commentThread.Comment - if comment.Location != nil { - updateLatest(comment.Location.Commit) - } - updateLatest(r.findLastCommit(startingCommit, latestCommit, commentThread.Children)) - } - return latestCommit -} - -func (r *Summary) getStartingCommit() string { - if r.Request.Alias != "" { - return r.Request.Alias - } - return r.Revision -} - -// GetHeadCommit returns the latest commit in a review. -func (r *Review) GetHeadCommit() (string, error) { - currentCommit := r.getStartingCommit() - if r.Request.ReviewRef == "" { - return currentCommit, nil - } - - if r.Submitted { - // The review has already been submitted. - // Go through the list of comments and find the last commented upon commit. - return r.findLastCommit(currentCommit, currentCommit, r.Comments), nil - } - - // It is possible that the review ref is no longer an ancestor of the starting - // commit (e.g. if a rebase left us in a detached head), in which case we have to - // find the head commit without using it. - useReviewRef, err := r.Repo.IsAncestor(currentCommit, r.Request.ReviewRef) - if err != nil { - return "", err - } - if useReviewRef { - return r.Repo.ResolveRefCommit(r.Request.ReviewRef) - } - - return r.findLastCommit(currentCommit, currentCommit, r.Comments), nil -} - -// GetBaseCommit returns the commit against which a review should be compared. -func (r *Review) GetBaseCommit() (string, error) { - if !r.IsOpen() { - if r.Request.BaseCommit != "" { - return r.Request.BaseCommit, nil - } - - // This means the review has been submitted, but did not specify a base commit. - // In this case, we have to treat the last parent commit as the base. This is - // usually what we want, since merging a target branch into a feature branch - // results in the previous commit to the feature branch being the first parent, - // and the latest commit to the target branch being the second parent. - return r.Repo.GetLastParent(r.Revision) - } - - targetRefHead, err := r.Repo.ResolveRefCommit(r.Request.TargetRef) - if err != nil { - return "", err - } - leftHandSide := targetRefHead - rightHandSide := r.Revision - if r.Request.ReviewRef != "" { - if reviewRefHead, err := r.Repo.ResolveRefCommit(r.Request.ReviewRef); err == nil { - rightHandSide = reviewRefHead - } - } - - return r.Repo.MergeBase(leftHandSide, rightHandSide) -} - -// ListCommits lists the commits included in a review. -func (r *Review) ListCommits() ([]string, error) { - baseCommit, err := r.GetBaseCommit() - if err != nil { - return nil, err - } - headCommit, err := r.GetHeadCommit() - if err != nil { - return nil, err - } - return r.Repo.ListCommitsBetween(baseCommit, headCommit) -} - -// GetDiff returns the diff for a review. -func (r *Review) GetDiff(diffArgs ...string) (string, error) { - var baseCommit, headCommit string - baseCommit, err := r.GetBaseCommit() - if err == nil { - headCommit, err = r.GetHeadCommit() - } - if err == nil { - return r.Repo.Diff(baseCommit, headCommit, diffArgs...) - } - return "", err -} - -// AddComment adds the given comment to the review. -func (r *Review) AddComment(c comment.Comment) error { - commentNote, err := c.Write() - if err != nil { - return err - } - - r.Repo.AppendNote(comment.Ref, r.Revision, commentNote) - return nil -} - -// Rebase performs an interactive rebase of the review onto its target ref. -// -// If the 'archivePrevious' argument is true, then the previous head of the -// review will be added to the 'refs/devtools/archives/reviews' ref prior -// to being rewritten. That ensures the review history is kept from being -// garbage collected. -func (r *Review) Rebase(archivePrevious bool) error { - if archivePrevious { - orig, err := r.GetHeadCommit() - if err != nil { - return err - } - if err := r.Repo.ArchiveRef(orig, archiveRef); err != nil { - return err - } - } - if err := r.Repo.SwitchToRef(r.Request.ReviewRef); err != nil { - return err - } - - err := r.Repo.RebaseRef(r.Request.TargetRef) - if err != nil { - return err - } - - alias, err := r.Repo.GetCommitHash("HEAD") - if err != nil { - return err - } - r.Request.Alias = alias - newNote, err := r.Request.Write() - if err != nil { - return err - } - return r.Repo.AppendNote(request.Ref, r.Revision, newNote) -} - -// RebaseAndSign performs an interactive rebase of the review onto its -// target ref. It signs the result of the rebase as well as (re)signs -// the review request itself. -// -// If the 'archivePrevious' argument is true, then the previous head of the -// review will be added to the 'refs/devtools/archives/reviews' ref prior -// to being rewritten. That ensures the review history is kept from being -// garbage collected. -func (r *Review) RebaseAndSign(archivePrevious bool) error { - if archivePrevious { - orig, err := r.GetHeadCommit() - if err != nil { - return err - } - if err := r.Repo.ArchiveRef(orig, archiveRef); err != nil { - return err - } - } - if err := r.Repo.SwitchToRef(r.Request.ReviewRef); err != nil { - return err - } - - err := r.Repo.RebaseAndSignRef(r.Request.TargetRef) - if err != nil { - return err - } - - alias, err := r.Repo.GetCommitHash("HEAD") - if err != nil { - return err - } - r.Request.Alias = alias - - key, err := r.Repo.GetUserSigningKey() - if err != nil { - return err - } - err = gpg.Sign(key, &r.Request) - if err != nil { - return err - } - - newNote, err := r.Request.Write() - if err != nil { - return err - } - return r.Repo.AppendNote(request.Ref, r.Revision, newNote) -} diff --git a/third_party/go/git-appraise/review/review_test.go b/third_party/go/git-appraise/review/review_test.go deleted file mode 100644 index af699afd9aeb..000000000000 --- a/third_party/go/git-appraise/review/review_test.go +++ /dev/null @@ -1,870 +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 review - -import ( - "github.com/google/git-appraise/repository" - "github.com/google/git-appraise/review/comment" - "github.com/google/git-appraise/review/request" - "sort" - "testing" -) - -func TestCommentSorting(t *testing.T) { - sampleComments := []*comment.Comment{ - &comment.Comment{ - Timestamp: "012400", - Description: "Fourth", - }, - &comment.Comment{ - Timestamp: "012400", - Description: "Fifth", - }, - &comment.Comment{ - Timestamp: "012346", - Description: "Second", - }, - &comment.Comment{ - Timestamp: "012345", - Description: "First", - }, - &comment.Comment{ - Timestamp: "012347", - Description: "Third", - }, - } - sort.Stable(commentsByTimestamp(sampleComments)) - descriptions := []string{} - for _, comment := range sampleComments { - descriptions = append(descriptions, comment.Description) - } - if !(descriptions[0] == "First" && descriptions[1] == "Second" && descriptions[2] == "Third" && descriptions[3] == "Fourth" && descriptions[4] == "Fifth") { - t.Fatalf("Comment ordering failed. Got %v", sampleComments) - } -} - -func TestThreadSorting(t *testing.T) { - sampleThreads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012400", - Description: "Fourth", - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012400", - Description: "Fifth", - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Description: "Second", - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Description: "First", - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012347", - Description: "Third", - }, - }, - } - sort.Stable(byTimestamp(sampleThreads)) - descriptions := []string{} - for _, thread := range sampleThreads { - descriptions = append(descriptions, thread.Comment.Description) - } - if !(descriptions[0] == "First" && descriptions[1] == "Second" && descriptions[2] == "Third" && descriptions[3] == "Fourth" && descriptions[4] == "Fifth") { - t.Fatalf("Comment thread ordering failed. Got %v", sampleThreads) - } -} - -func TestRequestSorting(t *testing.T) { - sampleRequests := []request.Request{ - request.Request{ - Timestamp: "012400", - Description: "Fourth", - }, - request.Request{ - Timestamp: "012400", - Description: "Fifth", - }, - request.Request{ - Timestamp: "012346", - Description: "Second", - }, - request.Request{ - Timestamp: "012345", - Description: "First", - }, - request.Request{ - Timestamp: "012347", - Description: "Third", - }, - } - sort.Stable(requestsByTimestamp(sampleRequests)) - descriptions := []string{} - for _, r := range sampleRequests { - descriptions = append(descriptions, r.Description) - } - if !(descriptions[0] == "First" && descriptions[1] == "Second" && descriptions[2] == "Third" && descriptions[3] == "Fourth" && descriptions[4] == "Fifth") { - t.Fatalf("Review request ordering failed. Got %v", sampleRequests) - } -} - -func validateUnresolved(t *testing.T, resolved *bool) { - if resolved != nil { - t.Fatalf("Expected resolved status to be unset, but instead it was %v", *resolved) - } -} - -func validateAccepted(t *testing.T, resolved *bool) { - if resolved == nil { - t.Fatal("Expected resolved status to be true, but it was unset") - } - if !*resolved { - t.Fatal("Expected resolved status to be true, but it was false") - } -} - -func validateRejected(t *testing.T, resolved *bool) { - if resolved == nil { - t.Fatal("Expected resolved status to be false, but it was unset") - } - if *resolved { - t.Fatal("Expected resolved status to be false, but it was true") - } -} - -func (commentThread *CommentThread) validateUnresolved(t *testing.T) { - validateUnresolved(t, commentThread.Resolved) -} - -func (commentThread *CommentThread) validateAccepted(t *testing.T) { - validateAccepted(t, commentThread.Resolved) -} - -func (commentThread *CommentThread) validateRejected(t *testing.T) { - validateRejected(t, commentThread.Resolved) -} - -func TestSimpleAcceptedThreadStatus(t *testing.T) { - resolved := true - simpleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &resolved, - }, - } - simpleThread.updateResolvedStatus() - simpleThread.validateAccepted(t) -} - -func TestSimpleRejectedThreadStatus(t *testing.T) { - resolved := false - simpleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &resolved, - }, - } - simpleThread.updateResolvedStatus() - simpleThread.validateRejected(t) -} - -func TestFYIThenAcceptedThreadStatus(t *testing.T) { - accepted := true - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: nil, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &accepted, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateUnresolved(t) -} - -func TestFYIThenFYIThreadStatus(t *testing.T) { - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: nil, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: nil, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateUnresolved(t) -} - -func TestFYIThenRejectedThreadStatus(t *testing.T) { - rejected := false - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: nil, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &rejected, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateRejected(t) -} - -func TestAcceptedThenAcceptedThreadStatus(t *testing.T) { - accepted := true - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &accepted, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &accepted, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateAccepted(t) -} - -func TestAcceptedThenFYIThreadStatus(t *testing.T) { - accepted := true - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &accepted, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: nil, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateAccepted(t) -} - -func TestAcceptedThenRejectedThreadStatus(t *testing.T) { - accepted := true - rejected := false - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &accepted, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &rejected, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateRejected(t) -} - -func TestRejectedThenAcceptedThreadStatus(t *testing.T) { - accepted := true - rejected := false - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &rejected, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &accepted, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateUnresolved(t) -} - -func TestRejectedThenFYIThreadStatus(t *testing.T) { - rejected := false - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &rejected, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: nil, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateRejected(t) -} - -func TestRejectedThenRejectedThreadStatus(t *testing.T) { - rejected := false - sampleThread := CommentThread{ - Comment: comment.Comment{ - Resolved: &rejected, - }, - Children: []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &rejected, - }, - }, - }, - } - sampleThread.updateResolvedStatus() - sampleThread.validateRejected(t) -} - -func TestRejectedThenAcceptedThreadsStatus(t *testing.T) { - accepted := true - rejected := false - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &rejected, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: &accepted, - }, - }, - } - status := updateThreadsStatus(threads) - validateRejected(t, status) -} - -func TestRejectedThenFYIThreadsStatus(t *testing.T) { - rejected := false - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &rejected, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: nil, - }, - }, - } - status := updateThreadsStatus(threads) - validateRejected(t, status) -} - -func TestRejectedThenRejectedThreadsStatus(t *testing.T) { - rejected := false - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &rejected, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: &rejected, - }, - }, - } - status := updateThreadsStatus(threads) - validateRejected(t, status) -} - -func TestAcceptedThenAcceptedThreadsStatus(t *testing.T) { - accepted := true - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &accepted, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: &accepted, - }, - }, - } - status := updateThreadsStatus(threads) - validateAccepted(t, status) -} - -func TestAcceptedThenFYIThreadsStatus(t *testing.T) { - accepted := true - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &accepted, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: nil, - }, - }, - } - status := updateThreadsStatus(threads) - validateAccepted(t, status) -} - -func TestAcceptedThenRejectedThreadsStatus(t *testing.T) { - accepted := true - rejected := false - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: &accepted, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: &rejected, - }, - }, - } - status := updateThreadsStatus(threads) - validateRejected(t, status) -} - -func TestFYIThenAcceptedThreadsStatus(t *testing.T) { - accepted := true - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: nil, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: &accepted, - }, - }, - } - status := updateThreadsStatus(threads) - validateAccepted(t, status) -} - -func TestFYIThenFYIThreadsStatus(t *testing.T) { - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: nil, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: nil, - }, - }, - } - status := updateThreadsStatus(threads) - validateUnresolved(t, status) -} - -func TestFYIThenRejectedThreadsStatus(t *testing.T) { - rejected := false - threads := []CommentThread{ - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012345", - Resolved: nil, - }, - }, - CommentThread{ - Comment: comment.Comment{ - Timestamp: "012346", - Resolved: &rejected, - }, - }, - } - status := updateThreadsStatus(threads) - validateRejected(t, status) -} - -func TestBuildCommentThreads(t *testing.T) { - rejected := false - accepted := true - root := comment.Comment{ - Timestamp: "012345", - Resolved: nil, - Description: "root", - } - rootHash, err := root.Hash() - if err != nil { - t.Fatal(err) - } - child := comment.Comment{ - Timestamp: "012346", - Resolved: nil, - Parent: rootHash, - Description: "child", - } - childHash, err := child.Hash() - updatedChild := comment.Comment{ - Timestamp: "012346", - Resolved: &rejected, - Original: childHash, - Description: "updated child", - } - updatedChildHash, err := updatedChild.Hash() - if err != nil { - t.Fatal(err) - } - leaf := comment.Comment{ - Timestamp: "012347", - Resolved: &accepted, - Parent: childHash, - Description: "leaf", - } - leafHash, err := leaf.Hash() - if err != nil { - t.Fatal(err) - } - commentsByHash := map[string]comment.Comment{ - rootHash: root, - childHash: child, - updatedChildHash: updatedChild, - leafHash: leaf, - } - threads := buildCommentThreads(commentsByHash) - if len(threads) != 1 { - t.Fatalf("Unexpected threads: %v", threads) - } - rootThread := threads[0] - if rootThread.Comment.Description != "root" { - t.Fatalf("Unexpected root thread: %v", rootThread) - } - if !rootThread.Edited { - t.Fatalf("Unexpected root thread edited status: %v", rootThread) - } - if len(rootThread.Children) != 1 { - t.Fatalf("Unexpected root children: %v", rootThread.Children) - } - rootChild := rootThread.Children[0] - if rootChild.Comment.Description != "updated child" { - t.Fatalf("Unexpected updated child: %v", rootChild) - } - if rootChild.Original.Description != "child" { - t.Fatalf("Unexpected original child: %v", rootChild) - } - if len(rootChild.Edits) != 1 { - t.Fatalf("Unexpected child history: %v", rootChild.Edits) - } - if len(rootChild.Children) != 1 { - t.Fatalf("Unexpected leaves: %v", rootChild.Children) - } - threadLeaf := rootChild.Children[0] - if threadLeaf.Comment.Description != "leaf" { - t.Fatalf("Unexpected leaf: %v", threadLeaf) - } - if len(threadLeaf.Children) != 0 { - t.Fatalf("Unexpected leaf children: %v", threadLeaf.Children) - } - if threadLeaf.Edited { - t.Fatalf("Unexpected leaf edited status: %v", threadLeaf) - } -} - -func TestGetHeadCommit(t *testing.T) { - repo := repository.NewMockRepoForTest() - - submittedSimpleReview, err := Get(repo, repository.TestCommitB) - if err != nil { - t.Fatal(err) - } - submittedSimpleReviewHead, err := submittedSimpleReview.GetHeadCommit() - if err != nil { - t.Fatal("Unable to compute the head commit for a known review of a simple commit: ", err) - } - if submittedSimpleReviewHead != repository.TestCommitB { - t.Fatal("Unexpected head commit computed for a known review of a simple commit.") - } - - submittedModifiedReview, err := Get(repo, repository.TestCommitD) - if err != nil { - t.Fatal(err) - } - submittedModifiedReviewHead, err := submittedModifiedReview.GetHeadCommit() - if err != nil { - t.Fatal("Unable to compute the head commit for a known, multi-commit review: ", err) - } - if submittedModifiedReviewHead != repository.TestCommitE { - t.Fatal("Unexpected head commit for a known, multi-commit review.") - } - - pendingReview, err := Get(repo, repository.TestCommitG) - if err != nil { - t.Fatal(err) - } - pendingReviewHead, err := pendingReview.GetHeadCommit() - if err != nil { - t.Fatal("Unable to compute the head commit for a known review of a merge commit: ", err) - } - if pendingReviewHead != repository.TestCommitI { - t.Fatal("Unexpected head commit computed for a pending review.") - } -} - -func TestGetBaseCommit(t *testing.T) { - repo := repository.NewMockRepoForTest() - - submittedSimpleReview, err := Get(repo, repository.TestCommitB) - if err != nil { - t.Fatal(err) - } - submittedSimpleReviewBase, err := submittedSimpleReview.GetBaseCommit() - if err != nil { - t.Fatal("Unable to compute the base commit for a known review of a simple commit: ", err) - } - if submittedSimpleReviewBase != repository.TestCommitA { - t.Fatal("Unexpected base commit computed for a known review of a simple commit.") - } - - submittedMergeReview, err := Get(repo, repository.TestCommitD) - if err != nil { - t.Fatal(err) - } - submittedMergeReviewBase, err := submittedMergeReview.GetBaseCommit() - if err != nil { - t.Fatal("Unable to compute the base commit for a known review of a merge commit: ", err) - } - if submittedMergeReviewBase != repository.TestCommitC { - t.Fatal("Unexpected base commit computed for a known review of a merge commit.") - } - - pendingReview, err := Get(repo, repository.TestCommitG) - if err != nil { - t.Fatal(err) - } - pendingReviewBase, err := pendingReview.GetBaseCommit() - if err != nil { - t.Fatal("Unable to compute the base commit for a known review of a merge commit: ", err) - } - if pendingReviewBase != repository.TestCommitF { - t.Fatal("Unexpected base commit computed for a pending review.") - } - - abandonRequest := pendingReview.Request - abandonRequest.TargetRef = "" - abandonNote, err := abandonRequest.Write() - if err != nil { - t.Fatal(err) - } - if err := repo.AppendNote(request.Ref, repository.TestCommitG, abandonNote); err != nil { - t.Fatal(err) - } - abandonedReview, err := Get(repo, repository.TestCommitG) - if err != nil { - t.Fatal(err) - } - if abandonedReview.IsOpen() { - t.Fatal("Failed to update a review to be abandoned") - } - abandonedReviewBase, err := abandonedReview.GetBaseCommit() - if err != nil { - t.Fatal("Unable to compute the base commit for an abandoned review: ", err) - } - if abandonedReviewBase != repository.TestCommitE { - t.Fatal("Unexpected base commit computed for an abandoned review.") - } -} - -func TestGetRequests(t *testing.T) { - repo := repository.NewMockRepoForTest() - pendingReview, err := Get(repo, repository.TestCommitG) - if err != nil { - t.Fatal(err) - } - if len(pendingReview.AllRequests) != 3 || pendingReview.Request.Description != "Final description of G" { - t.Fatal("Unexpected requests for a pending review: ", pendingReview.AllRequests, pendingReview.Request) - } -} - -func TestRebase(t *testing.T) { - repo := repository.NewMockRepoForTest() - pendingReview, err := Get(repo, repository.TestCommitG) - if err != nil { - t.Fatal(err) - } - - // Rebase the review and then confirm that it has been updated correctly. - if err := pendingReview.Rebase(true); err != nil { - t.Fatal(err) - } - reviewJSON, err := pendingReview.GetJSON() - if err != nil { - t.Fatal(err) - } - headRef, err := repo.GetHeadRef() - if err != nil { - t.Fatal(err) - } - if headRef != pendingReview.Request.ReviewRef { - t.Fatal("Failed to switch to the review ref during a rebase") - } - isAncestor, err := repo.IsAncestor(pendingReview.Revision, archiveRef) - if err != nil { - t.Fatal(err) - } - if !isAncestor { - t.Fatalf("Commit %q is not archived", pendingReview.Revision) - } - reviewCommit, err := repo.GetCommitHash(pendingReview.Request.ReviewRef) - if err != nil { - t.Fatal(err) - } - reviewAlias := pendingReview.Request.Alias - if reviewAlias == "" || reviewAlias == pendingReview.Revision || reviewCommit != reviewAlias { - t.Fatalf("Failed to set the review alias: %q", reviewJSON) - } - - // Submit the review. - if err := repo.SwitchToRef(pendingReview.Request.TargetRef); err != nil { - t.Fatal(err) - } - if err := repo.MergeRef(pendingReview.Request.ReviewRef, true); err != nil { - t.Fatal(err) - } - - // Reread the review and confirm that it has been submitted. - submittedReview, err := Get(repo, pendingReview.Revision) - if err != nil { - t.Fatal(err) - } - submittedReviewJSON, err := submittedReview.GetJSON() - if err != nil { - t.Fatal(err) - } - if !submittedReview.Submitted { - t.Fatalf("Failed to submit the review: %q", submittedReviewJSON) - } -} - -func TestRebaseDetachedHead(t *testing.T) { - repo := repository.NewMockRepoForTest() - pendingReview, err := Get(repo, repository.TestCommitG) - if err != nil { - t.Fatal(err) - } - - // Switch the review to having a review ref that is not a branch. - pendingReview.Request.ReviewRef = repository.TestAlternateReviewRef - newNote, err := pendingReview.Request.Write() - if err != nil { - t.Fatal(err) - } - if err := repo.AppendNote(request.Ref, pendingReview.Revision, newNote); err != nil { - t.Fatal(err) - } - pendingReview, err = Get(repo, repository.TestCommitG) - if err != nil { - t.Fatal(err) - } - - // Rebase the review and then confirm that it has been updated correctly. - if err := pendingReview.Rebase(true); err != nil { - t.Fatal(err) - } - headRef, err := repo.GetHeadRef() - if err != nil { - t.Fatal(err) - } - if headRef != pendingReview.Request.Alias { - t.Fatal("Failed to switch to a detached head during a rebase") - } - isAncestor, err := repo.IsAncestor(pendingReview.Revision, archiveRef) - if err != nil { - t.Fatal(err) - } - if !isAncestor { - t.Fatalf("Commit %q is not archived", pendingReview.Revision) - } - - // Submit the review. - if err := repo.SwitchToRef(pendingReview.Request.TargetRef); err != nil { - t.Fatal(err) - } - reviewHead, err := pendingReview.GetHeadCommit() - if err != nil { - t.Fatal(err) - } - if err := repo.MergeRef(reviewHead, true); err != nil { - t.Fatal(err) - } - - // Reread the review and confirm that it has been submitted. - submittedReview, err := Get(repo, pendingReview.Revision) - if err != nil { - t.Fatal(err) - } - submittedReviewJSON, err := submittedReview.GetJSON() - if err != nil { - t.Fatal(err) - } - if !submittedReview.Submitted { - t.Fatalf("Failed to submit the review: %q", submittedReviewJSON) - } -} diff --git a/third_party/go/git-appraise/schema/analysis.json b/third_party/go/git-appraise/schema/analysis.json deleted file mode 100644 index cbecb5416bc9..000000000000 --- a/third_party/go/git-appraise/schema/analysis.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - - "properties": { - "timestamp": { - "description": "the number of seconds since the Unix epoch", - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-9]{10,10}" - }, - - "status": { - "description": "represents the overall status of all messages from the analysis results", - "oneOf": [{ - "$ref": "#/definitions/lgtm" - }, { - "$ref": "#/definitions/fyi" - }, { - "$ref": "#/definitions/nmw" - }] - }, - - "url": { - "description": "a publicly readable file, which contains JSON formatted analysis results. Those results should conform to the JSON format of the ShipshapeResponse protocol buffer message defined https://github.com/google/shipshape/blob/master/shipshape/proto/shipshape_rpc.proto", - "type": "string" - }, - - "v": { - "type": "integer", - "enum": [0] - } - }, - - "required": [ - "timestamp", - "url" - ], - - "definitions": { - "lgtm": { - "title": "Looks Good To Me", - "description": "indicates the analysis produced no messages", - "type": "string", - "enum": ["lgtm"] - }, - "fyi": { - "title": "For your information", - "description": "indicates the analysis produced some messages, but none of them indicate errors", - "type": "string", - "enum": ["fyi"] - }, - "nmw": { - "title": "Needs more work", - "description": "indicates the analysis produced at least one message indicating an error", - "type": "string", - "enum": ["nmw"] - } - } -} diff --git a/third_party/go/git-appraise/schema/ci.json b/third_party/go/git-appraise/schema/ci.json deleted file mode 100644 index 7436408290ce..000000000000 --- a/third_party/go/git-appraise/schema/ci.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - - "properties": { - "timestamp": { - "description": "the number of seconds since the Unix epoch", - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-9]{10,10}" - }, - - "agent": { - "description": "a free-form string that identifies the build and test runner", - "type": "string" - }, - - "status": { - "description": "the final status of a build or test", - "type": "string", - "enum": [ - "success", - "failure" - ] - }, - - "url": { - "type": "string" - }, - - "v": { - "type": "integer", - "enum": [0] - } - }, - - "required": [ - "timestamp", - "agent" - ] -} diff --git a/third_party/go/git-appraise/schema/comment.json b/third_party/go/git-appraise/schema/comment.json deleted file mode 100644 index a39b1a2e670b..000000000000 --- a/third_party/go/git-appraise/schema/comment.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - - "properties": { - "timestamp": { - "description": "the number of seconds since the Unix epoch", - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-9]{10,10}" - }, - - "author": { - "type": "string" - }, - - "original": { - "description": "the SHA1 hash of another comment on the same revision, and it means this comment is an updated version of that comment", - "type": "string" - }, - - "parent": { - "description": "the SHA1 hash of another comment on the same revision, and it means this comment is a reply to that comment", - "type": "string" - }, - - "location": { - "type": "object", - "properties": { - "commit": { - "type": "string" - }, - "path": { - "type": "string" - }, - "range": { - "type": "object", - "properties": { - "startLine": { - "type": "integer" - }, - "startColumn": { - "type": "integer" - }, - "endLine": { - "type": "integer" - }, - "endColumn": { - "type": "integer" - } - } - } - } - }, - - "description": { - "type": "string" - }, - - "resolved": { - "type": "boolean" - }, - - "v": { - "type": "integer", - "enum": [0] - } - }, - - "required": [ - "timestamp", - "author" - ] -} diff --git a/third_party/go/git-appraise/schema/request.json b/third_party/go/git-appraise/schema/request.json deleted file mode 100644 index 9ec022a16e9e..000000000000 --- a/third_party/go/git-appraise/schema/request.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - - "properties": { - "timestamp": { - "description": "the number of seconds since the Unix epoch", - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-9]{10,10}" - }, - - "requester": { - "type": "string" - }, - - "baseCommit": { - "type": "string" - }, - - "reviewRef": { - "description": "used to specify a git ref that tracks the current revision under review", - "type": "string" - }, - - "targetRef": { - "description": "used to specify the git ref that should be updated once the review is approved", - "type": "string" - }, - - "reviewers": { - "type": "array", - "items": { - "type": "string" - } - }, - - "description": { - "type": "string" - }, - - "v": { - "type": "integer", - "enum": [0] - }, - - "alias": { - "description": "used to specify a post-rebase commit hash for the review", - "type": "string" - } - }, - - "required": [ - "timestamp", - "requester" - ] -} |