about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--default.nix2
-rw-r--r--third_party/go/git-appraise/.gitignore2
-rw-r--r--third_party/go/git-appraise/CONTRIBUTING.md24
-rw-r--r--third_party/go/git-appraise/LICENSE202
-rw-r--r--third_party/go/git-appraise/README.md158
-rw-r--r--third_party/go/git-appraise/commands/abandon.go139
-rw-r--r--third_party/go/git-appraise/commands/accept.go109
-rw-r--r--third_party/go/git-appraise/commands/commands.go55
-rw-r--r--third_party/go/git-appraise/commands/comment.go165
-rw-r--r--third_party/go/git-appraise/commands/input/input.go118
-rw-r--r--third_party/go/git-appraise/commands/list.go74
-rw-r--r--third_party/go/git-appraise/commands/output/output.go216
-rw-r--r--third_party/go/git-appraise/commands/pull.go93
-rw-r--r--third_party/go/git-appraise/commands/push.go49
-rw-r--r--third_party/go/git-appraise/commands/rebase.go100
-rw-r--r--third_party/go/git-appraise/commands/reject.go119
-rw-r--r--third_party/go/git-appraise/commands/request.go182
-rw-r--r--third_party/go/git-appraise/commands/request_test.go36
-rw-r--r--third_party/go/git-appraise/commands/show.go85
-rw-r--r--third_party/go/git-appraise/commands/submit.go157
-rw-r--r--third_party/go/git-appraise/docs/tutorial.md404
-rw-r--r--third_party/go/git-appraise/git-appraise/default.nix18
-rw-r--r--third_party/go/git-appraise/git-appraise/deps.nix3
-rw-r--r--third_party/go/git-appraise/git-appraise/git-appraise.go104
-rw-r--r--third_party/go/git-appraise/repository/git.go987
-rw-r--r--third_party/go/git-appraise/repository/git_test.go94
-rw-r--r--third_party/go/git-appraise/repository/mock_repo.go613
-rw-r--r--third_party/go/git-appraise/repository/repo.go221
-rw-r--r--third_party/go/git-appraise/review/analyses/analyses.go160
-rw-r--r--third_party/go/git-appraise/review/analyses/analyses_test.go77
-rw-r--r--third_party/go/git-appraise/review/ci/ci.go95
-rw-r--r--third_party/go/git-appraise/review/ci/ci_test.go85
-rw-r--r--third_party/go/git-appraise/review/comment/comment.go266
-rw-r--r--third_party/go/git-appraise/review/gpg/signable.go129
-rw-r--r--third_party/go/git-appraise/review/request/request.go104
-rw-r--r--third_party/go/git-appraise/review/review.go772
-rw-r--r--third_party/go/git-appraise/review/review_test.go870
-rw-r--r--third_party/go/git-appraise/schema/analysis.json61
-rw-r--r--third_party/go/git-appraise/schema/ci.json42
-rw-r--r--third_party/go/git-appraise/schema/comment.json75
-rw-r--r--third_party/go/git-appraise/schema/request.json58
-rwxr-xr-xtools/bin/__dispatch.sh3
l---------tools/bin/git-appraise1
43 files changed, 0 insertions, 7327 deletions
diff --git a/default.nix b/default.nix
index ccb301d68f4e..ed6258108d5b 100644
--- a/default.nix
+++ b/default.nix
@@ -31,8 +31,6 @@ let
     };
 
     # Third-party projects (either vendored or modified from nixpkgs) go here:
-    gitAppraise = self.callPackage ./third_party/go/git-appraise/git-appraise {};
-
     nixery = import ./third_party/nixery.nix { pkgs = self; };
     terraform-gcp = unstable.terraform_0_12.withPlugins(p: [ p.google p.google-beta ]);
     ormolu = import (self.fetchFromGitHub {
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, &notesMapping{
-			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 &notesOverview{
-		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"
-  ]
-}
diff --git a/tools/bin/__dispatch.sh b/tools/bin/__dispatch.sh
index 25fb621c01f2..09b404f3b33d 100755
--- a/tools/bin/__dispatch.sh
+++ b/tools/bin/__dispatch.sh
@@ -10,9 +10,6 @@ readonly REPO_ROOT=$(git rev-parse --show-toplevel)
 readonly TARGET_TOOL=$(basename $0)
 
 case "${TARGET_TOOL}" in
-  git-appraise)
-    attr="thirdParty.gitAppraise"
-    ;;
   terraform)
     attr="terraform-gcp"
     ;;
diff --git a/tools/bin/git-appraise b/tools/bin/git-appraise
deleted file mode 120000
index 8390ec9c9652..000000000000
--- a/tools/bin/git-appraise
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file