├── .gitignore ├── Makefile ├── go.mod ├── pkg └── gitls │ ├── git-ls.go │ ├── gists.go │ ├── user.go │ ├── repos.go │ └── plunder.go ├── main.go ├── readme.md ├── .github └── workflows │ └── workflow.yml └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | git-ls 3 | data/* 4 | gitls_* 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APP=gitls 2 | FLAGS=-trimpath -ldflags="-s -w -buildid=" 3 | PLATFORMS=windows linux darwin 4 | OS=$(word 1, $@) 5 | 6 | all: ${PLATFORMS} 7 | 8 | ${PLATFORMS}: 9 | GOOS=${OS} go build ${FLAGS} -o ${APP}_${OS} 10 | 11 | clean: 12 | rm ${APP}_* 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/audibleblink/git-ls 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/briandowns/spinner v1.6.1 7 | github.com/google/go-github/v32 v32.1.0 8 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 9 | gopkg.in/src-d/go-git.v4 v4.13.1 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/gitls/git-ls.go: -------------------------------------------------------------------------------- 1 | package gitls 2 | 3 | import ( 4 | "context" 5 | "net/url" 6 | 7 | "github.com/google/go-github/v32/github" 8 | "golang.org/x/oauth2" 9 | ) 10 | 11 | const ( 12 | firstPage = 1 13 | lastPage = 0 14 | maxPerPage = 100 15 | ) 16 | 17 | type ghClient struct { 18 | gh *github.Client 19 | Token string 20 | } 21 | 22 | // NewClient returns a GitHub client for the application to use 23 | func NewClient(apiKey string) (client *ghClient) { 24 | client = &ghClient{ 25 | Token: apiKey, 26 | } 27 | 28 | ctx := context.Background() 29 | ts := oauth2.StaticTokenSource( 30 | &oauth2.Token{AccessToken: apiKey}, 31 | ) 32 | tc := oauth2.NewClient(ctx, ts) 33 | client.gh = github.NewClient(tc) 34 | return 35 | } 36 | 37 | // NewEnterpriseClient returns a client with a custom github API base URL 38 | func NewEnterpriseClient(baseURL, apiKey string) (client *ghClient) { 39 | client = NewClient(apiKey) 40 | base, _ := url.Parse(baseURL) 41 | client.gh.BaseURL = base 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/audibleblink/git-ls/pkg/gitls" 8 | ) 9 | 10 | func main() { 11 | token := os.Getenv("GITHUB_TOKEN") 12 | cli := gitls.NewClient(token) 13 | 14 | baseURL := os.Getenv("GITHUB_API_BASE_URL") 15 | if baseURL != "" { 16 | cli = gitls.NewEnterpriseClient(baseURL, token) 17 | } 18 | 19 | if len(os.Args) <= 1 { 20 | usage() 21 | os.Exit(0) 22 | } 23 | 24 | switch os.Args[1] { 25 | case "repos": 26 | cli.Repos() 27 | case "collabs": 28 | cli.Collabs() 29 | case "gists": 30 | cli.Gists() 31 | case "user": 32 | cli.User() 33 | case "export": 34 | privateOnly := false 35 | cli.Plunder(privateOnly) 36 | case "plunder": 37 | privateOnly := true 38 | cli.Plunder(privateOnly) 39 | default: 40 | fmt.Println("Not Implemented") 41 | usage() 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | func usage() { 47 | fmt.Fprintln(os.Stderr, ` 48 | Usage: git-ls 49 | 50 | Inspect properties of the token owner 51 | See all gists, public and private, to which this token owner has access 52 | See all repos, public and private, to which this token owner has access 53 | See other users' repos, public and private, to which this token owner has access 54 | Clone all repos, public and private (check your HD size first!) 55 | Clones all private repos the token can access with wreckless abandon`) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/gitls/gists.go: -------------------------------------------------------------------------------- 1 | package gitls 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | 9 | "github.com/google/go-github/v32/github" 10 | ) 11 | 12 | type gist struct { 13 | Owner string 14 | Descripiton string 15 | GitPullURL string 16 | Files []github.GistFilename 17 | Private bool 18 | } 19 | 20 | func (gls *ghClient) Gists() { 21 | allGists, err := gls.gists() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | out, err := json.MarshalIndent(allGists, "", " ") 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | fmt.Println(string(out)) 32 | } 33 | 34 | func (gls *ghClient) gists() (all []*gist, err error) { 35 | var ghGists []*github.Gist 36 | ghGists = gls.gistPager(ghGists, firstPage) 37 | 38 | for _, g := range ghGists { 39 | var filenames []github.GistFilename 40 | for name, _ := range g.Files { 41 | filenames = append(filenames, name) 42 | } 43 | gst := &gist{ 44 | Owner: g.GetOwner().GetLogin(), 45 | Descripiton: g.GetDescription(), 46 | GitPullURL: g.GetGitPullURL(), 47 | Private: !g.GetPublic(), 48 | Files: filenames, 49 | } 50 | all = append(all, gst) 51 | } 52 | return 53 | } 54 | 55 | func (gls *ghClient) gistPager(data []*github.Gist, page int) []*github.Gist { 56 | if page == lastPage { 57 | return data 58 | } 59 | 60 | ctx := context.Background() 61 | opts := &github.GistListOptions{} 62 | opts.PerPage = maxPerPage 63 | opts.Page = page 64 | 65 | gists, response, err := gls.gh.Gists.List(ctx, "", opts) 66 | if err != nil { 67 | return data 68 | } 69 | data = append(data, gists...) 70 | return gls.gistPager(data, response.NextPage) 71 | } 72 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # git-ls 2 | 3 | Explore github content given a valid token. 4 | 5 | ## Usage 6 | 7 | The token is read from the `GITHUB_TOKEN` environment variable. I chose this so I don't end up 8 | putting tokens in HISTFILEs. Your options are to source it from a file with `source .env`, export 9 | it in your shell session, or go ahead and prepend it like so: `GITHUB_TOKEN=xyz git-ls user`. 10 | 11 | Run `git-ls` with no arguments to see what data can be listed. 12 | 13 | ## Examples 14 | 15 | Below are some useful filters to use with `jq`, the CLI json parser. 16 | 17 | ```bash 18 | # Just private gists 19 | ❯❯ git-ls gists | jq '.[] | select(.Private == true)' 20 | 21 | # Just repo names 22 | ❯❯ git-ls repos | jq -r '.[].Name' 23 | 24 | # Repos that don't belong to the GITHUB_TOKEN owner 25 | ❯❯ git-ls repos | jq '.[] | select(.Owner != "audibleblink")' 26 | # or 27 | ❯❯ git-ls collabs 28 | ``` 29 | 30 | ## Properties 31 | 32 | I tried to select the most useful properties returned by the GitHub API to keep noise low. 33 | These are the properties that you can filter on with `jq` 34 | 35 | ### Gists 36 | 37 | * Owner 38 | * Description 39 | * GitPullUrl 40 | * Files 41 | * Private 42 | 43 | ### Repos 44 | 45 | * Name 46 | * Description 47 | * URL 48 | * Owner 49 | * Organization 50 | * StargazersConut 51 | * Private 52 | 53 | 54 | ## Accessing Data 55 | If you have the API token that got you this far, 56 | it can be placed into an HTTP clone request. 57 | 58 | ```bash 59 | git clone https://${GITHUB_TOKEN}@github.com/someOrg/someRepo 60 | ``` 61 | 62 | If you just want all the secret repos the token has access to 63 | 64 | ```bash 65 | git-ls plunder 66 | ``` 67 | To export all repos, regardless of them being private or public: 68 | 69 | ```bash 70 | git-ls export 71 | ``` 72 | 73 | ![](https://i.imgur.com/lcn6Wop.png) 74 | 75 | ![](https://i.imgur.com/s587JPU.png) 76 | -------------------------------------------------------------------------------- /pkg/gitls/user.go: -------------------------------------------------------------------------------- 1 | package gitls 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | ) 9 | 10 | type user struct { 11 | Bio string 12 | Company string 13 | Email string 14 | Location string 15 | Login string 16 | Name string 17 | Type string 18 | Followers int 19 | OwnedPrivateRepos int 20 | PublicRepos int 21 | PublicGists int 22 | PrivateGists int 23 | TotalPrivateRepos int 24 | TwoFactorAuthentication bool 25 | } 26 | 27 | func (gls *ghClient) TokenOwner() string { 28 | usr, err := gls.user() 29 | if err != nil { 30 | return "Unknown User" 31 | } 32 | return usr.Login 33 | } 34 | 35 | func (gls *ghClient) User() { 36 | usr, err := gls.user() 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | out, err := json.MarshalIndent(usr, "", " ") 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | fmt.Printf(string(out)) 45 | } 46 | 47 | func (gls *ghClient) user() (self *user, err error) { 48 | usr, _, err := gls.gh.Users.Get(context.Background(), "") 49 | if err != nil { 50 | return 51 | } 52 | self = &user{ 53 | Bio: usr.GetBio(), 54 | Company: usr.GetCompany(), 55 | Email: usr.GetEmail(), 56 | Location: usr.GetLocation(), 57 | Login: usr.GetLogin(), 58 | Name: usr.GetName(), 59 | Type: usr.GetType(), 60 | Followers: usr.GetFollowers(), 61 | OwnedPrivateRepos: usr.GetOwnedPrivateRepos(), 62 | PublicRepos: usr.GetPublicRepos(), 63 | PublicGists: usr.GetPublicGists(), 64 | PrivateGists: usr.GetPrivateGists(), 65 | TotalPrivateRepos: usr.GetTotalPrivateRepos(), 66 | TwoFactorAuthentication: usr.GetTwoFactorAuthentication(), 67 | } 68 | return 69 | } 70 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | 6 | name: Build and Release 7 | 8 | jobs: 9 | 10 | release: 11 | name: 'Create Release from Tag' 12 | runs-on: ubuntu-latest 13 | outputs: 14 | upload_url: ${{ steps.create_release.outputs.upload_url }} 15 | steps: 16 | 17 | - name: Checkout 18 | uses: actions/checkout@master 19 | 20 | - name: Get the version 21 | id: get_version 22 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} 23 | 24 | - name: Create Release 25 | id: create_release 26 | uses: actions/create-release@v1 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | with: 30 | tag_name: ${{ steps.get_version.outputs.VERSION }} 31 | release_name: Release ${{ steps.get_version.outputs.VERSION }} 32 | draft: false 33 | prerelease: false 34 | 35 | build: 36 | name: 'Build and Upload Release Binary' 37 | runs-on: ubuntu-latest 38 | needs: release 39 | strategy: 40 | matrix: 41 | OSes: ['windows', 'darwin', 'linux'] 42 | arch: ['amd64'] 43 | 44 | steps: 45 | - name: 'Checkout' 46 | uses: actions/checkout@master 47 | 48 | - name: 'Setup Golang Environment' 49 | uses: actions/setup-go@v2 50 | with: 51 | go-version: '^1.17' 52 | 53 | - name: 'Build ${{ matrix.OSes }}-${{ matrix.arch }}' 54 | run: | 55 | GOOS=${{ matrix.OSes }} \ 56 | GOARCH=${{ matrix.arch }} \ 57 | go build -trimpath -ldflags="-s -w -buildid=" \ 58 | -o gitls_${{ matrix.OSes }}_${{ matrix.arch }} 59 | 60 | - name: 'Upload Release Assets' 61 | uses: actions/upload-release-asset@v1 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 64 | with: 65 | upload_url: ${{ needs.release.outputs.upload_url }} 66 | asset_path: ./gitls_${{ matrix.OSes }}_${{ matrix.arch }} 67 | asset_name: gitls_${{ matrix.OSes }}_${{ matrix.arch }} 68 | asset_content_type: application/octet-stream 69 | 70 | -------------------------------------------------------------------------------- /pkg/gitls/repos.go: -------------------------------------------------------------------------------- 1 | package gitls 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "regexp" 9 | 10 | "github.com/google/go-github/v32/github" 11 | ) 12 | 13 | type repo struct { 14 | Name string 15 | Descripiton string 16 | URL string 17 | Owner string 18 | Organization string 19 | StargazersCount int 20 | Private bool 21 | } 22 | 23 | func (r *repo) CloneableURL(token string) (cloneURL string) { 24 | re1, _ := regexp.Compile("api\\.") 25 | re2, _ := regexp.Compile("repos/") 26 | 27 | temp := re1.ReplaceAllLiteralString(r.URL, fmt.Sprintf("%s@", token)) 28 | cloneURL = re2.ReplaceAllLiteralString(temp, "") 29 | return 30 | } 31 | 32 | func (gls *ghClient) Collabs() { 33 | allRepos, err := gls.repos() 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | tokenOwner := gls.TokenOwner() 39 | var filteredRepos []*repo 40 | for _, r := range allRepos { 41 | if r.Owner != tokenOwner { 42 | filteredRepos = append(filteredRepos, r) 43 | } 44 | } 45 | 46 | out, err := json.MarshalIndent(filteredRepos, "", " ") 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | fmt.Println(string(out)) 52 | } 53 | 54 | func (gls *ghClient) Repos() { 55 | allRepos, err := gls.repos() 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | out, err := json.MarshalIndent(allRepos, "", " ") 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | 65 | fmt.Println(string(out)) 66 | } 67 | 68 | func (gls *ghClient) repos() (all []*repo, err error) { 69 | var ghRepos []*github.Repository 70 | ghRepos = gls.repoPager(ghRepos, firstPage) 71 | 72 | for _, ghRepo := range ghRepos { 73 | repository := &repo{ 74 | Name: ghRepo.GetName(), 75 | Descripiton: ghRepo.GetDescription(), 76 | URL: ghRepo.GetURL(), 77 | Owner: ghRepo.GetOwner().GetLogin(), 78 | Organization: ghRepo.GetOrganization().GetName(), 79 | StargazersCount: ghRepo.GetStargazersCount(), 80 | Private: ghRepo.GetPrivate(), 81 | } 82 | all = append(all, repository) 83 | } 84 | return 85 | } 86 | 87 | func (gls *ghClient) repoPager(data []*github.Repository, page int) []*github.Repository { 88 | if page == lastPage { 89 | return data 90 | } 91 | 92 | ctx := context.Background() 93 | opts := &github.RepositoryListOptions{} 94 | opts.PerPage = maxPerPage 95 | opts.Page = page 96 | 97 | repos, response, err := gls.gh.Repositories.List(ctx, "", opts) 98 | if err != nil { 99 | return data 100 | } 101 | data = append(data, repos...) 102 | return gls.repoPager(data, response.NextPage) 103 | } 104 | -------------------------------------------------------------------------------- /pkg/gitls/plunder.go: -------------------------------------------------------------------------------- 1 | package gitls 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "sync" 8 | "time" 9 | 10 | "github.com/briandowns/spinner" 11 | "gopkg.in/src-d/go-git.v4" 12 | ) 13 | 14 | // Plunder will find all private repositories from a given token and clone them 15 | // into a given folder. 16 | func (gls *ghClient) Plunder(privateOnly bool) { 17 | owner := gls.TokenOwner() 18 | charSet := spinner.CharSets[14] 19 | spnTime := 100 * time.Millisecond 20 | 21 | msg := fmt.Sprintf("Seaching for private repos to which %s has access", owner) 22 | final := fmt.Sprintf("%s\nFound the following repos:\n", msg) 23 | 24 | spin := spinner.New(charSet, spnTime, setOpts(msg, final)) 25 | spin.Start() 26 | 27 | allRepos, err := gls.repos() 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | spin.Stop() 32 | 33 | var reposToClone []*repo 34 | if privateOnly { 35 | for _, repo := range allRepos { 36 | if repo.Private { 37 | fmt.Printf("%s/%s\n", repo.Owner, repo.Name) 38 | reposToClone = append(reposToClone, repo) 39 | } 40 | } 41 | } else { 42 | reposToClone = allRepos[:] 43 | } 44 | 45 | var wg sync.WaitGroup 46 | wg.Add(len(reposToClone)) 47 | 48 | final = stats(owner, reposToClone) 49 | spin = spinner.New(charSet, spnTime, setOpts("Cloning repos", final)) 50 | defer spin.Stop() 51 | spin.Start() 52 | 53 | for _, repo := range reposToClone { 54 | 55 | cloneURL := repo.CloneableURL(gls.Token) 56 | path := fmt.Sprintf("%s/%s", repo.Owner, repo.Name) 57 | 58 | go func(repoURL, dirPath string, w *sync.WaitGroup) { 59 | defer w.Done() 60 | err := CloneRepository(repoURL, dirPath) 61 | if err != nil { 62 | log.Println(err) 63 | } 64 | 65 | }(cloneURL, path, &wg) 66 | } 67 | wg.Wait() 68 | } 69 | 70 | // CloneRepository clones a repository 71 | func CloneRepository(url, destDir string) (err error) { 72 | err = os.MkdirAll(destDir, 0755) 73 | if err != nil { 74 | return 75 | } 76 | _, err = git.PlainClone(destDir, false, &git.CloneOptions{URL: url}) 77 | return 78 | } 79 | 80 | func setOpts(msg, final string) func(*spinner.Spinner) { 81 | return func(s *spinner.Spinner) { 82 | s.Suffix = fmt.Sprintf(" %s...", msg) 83 | s.FinalMSG = final 84 | s.Color("fgHiCyan") 85 | s.Writer = os.Stderr 86 | s.HideCursor = true 87 | } 88 | } 89 | 90 | func stats(owner string, repos []*repo) string { 91 | var owned []string 92 | var guest []string 93 | for _, repo := range repos { 94 | if owner == repo.Owner { 95 | owned = append(owned, repo.Name) 96 | continue 97 | } 98 | guest = append(guest, repo.Name) 99 | } 100 | 101 | return fmt.Sprintf(` 102 | Secret Repos owned by %s: %d 103 | Secret Repos owned by others: %d 104 | `, owner, len(owned), len(guest), 105 | ) 106 | } 107 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= 3 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= 4 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= 5 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 6 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 7 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 8 | github.com/briandowns/spinner v1.6.1 h1:LBxHu5WLyVuVEtTD72xegiC7QJGx598LBpo3ywKTapA= 9 | github.com/briandowns/spinner v1.6.1/go.mod h1://Zf9tMcxfRUA36V23M6YGEAv+kECGfvpnLTnb8n4XQ= 10 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 11 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 15 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 16 | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= 17 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 18 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= 19 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 20 | github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= 21 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 22 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 23 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 24 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 25 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 26 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 27 | github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= 28 | github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= 29 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 30 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 31 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 32 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 33 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 34 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= 35 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 36 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 37 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 38 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 39 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 40 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 41 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 42 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 43 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 44 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 45 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 46 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 47 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 48 | github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= 49 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 50 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 51 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 52 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 53 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= 54 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 55 | github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= 56 | github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= 57 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 58 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 59 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 60 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 61 | github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= 62 | github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= 63 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 64 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 65 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= 66 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 67 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 68 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 69 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 70 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 71 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 72 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= 73 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 74 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 75 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 76 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 77 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 78 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 79 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 80 | golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 81 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 82 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 83 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0= 84 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 85 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 86 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 87 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 88 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 89 | golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= 90 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 91 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 92 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 93 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 94 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 95 | gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= 96 | gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= 97 | gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= 98 | gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= 99 | gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= 100 | gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= 101 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 102 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 103 | --------------------------------------------------------------------------------