6
0
mirror of https://github.com/grdl/git-get.git synced 2026-02-05 01:29:42 +00:00

Remove gogit and major refactoring (#2)

* Fix typo in readme

* Reimplement all git methods without go-git

* Rename repo pkg to git, add gitconfig methods

* Improve tests for configuration reading

* Rename package file to io and move RepoFinder there

* Refactor printers

- Remove smart printer
- Decouple printers from git repos with interfaces
- Update printer functions
- Remove unnecessary flags
- Add better remote URL detection

* Update readme and go.mod

* Add author to git commit in tests

Otherwise tests will fail in CI.

* Install git before running tests and don't use cgo

* Add better error message, revert installing git

* Ensure commit message is in quotes

* Set up git config before running tests
This commit is contained in:
Grzegorz Dlugoszewski
2020-06-24 23:54:44 +02:00
committed by GitHub
parent 2ef739ea49
commit 8c132cdafa
26 changed files with 1452 additions and 1648 deletions

View File

@@ -3,140 +3,72 @@ package pkg
import (
"fmt"
"git-get/pkg/cfg"
"git-get/pkg/git"
"git-get/pkg/io"
"git-get/pkg/print"
"git-get/pkg/repo"
"os"
"sort"
"strings"
"syscall"
"github.com/karrick/godirwalk"
"github.com/pkg/errors"
)
// errSkipNode is used as an error indicating that .git directory has been found.
// It's handled by ErrorsCallback to tell the WalkCallback to skip this dir.
var errSkipNode = errors.New(".git directory found, skipping this node")
var repos []string
// ListCfg provides configuration for the List command.
type ListCfg struct {
Fetch bool
Output string
PrivateKey string
Root string
Fetch bool
Output string
Root string
}
// List executes the "git list" command.
func List(c *ListCfg) error {
paths, err := findRepos(c.Root)
paths, err := io.NewRepoFinder(c.Root).Find()
if err != nil {
return err
}
repos, err := openAll(paths)
if err != nil {
return err
// TODO: we should open, fetch and read status of each repo in separate goroutine
var repos []git.Repo
for _, path := range paths {
repo, err := git.Open(path)
if err != nil {
// TODO: how should we handle it?
continue
}
if c.Fetch {
err := repo.Fetch()
if err != nil {
// TODO: handle error
}
}
repos = append(repos, *repo)
}
var printer print.Printer
switch c.Output {
case cfg.OutFlat:
printer = &print.FlatPrinter{}
printables := make([]print.Repo, len(repos))
for i := range repos {
printables[i] = &repos[i]
}
fmt.Println(print.NewFlatPrinter().Print(printables))
case cfg.OutTree:
printer = &print.TreePrinter{}
case cfg.OutSmart:
printer = &print.SmartPrinter{}
printables := make([]print.Repo, len(repos))
for i := range repos {
printables[i] = &repos[i]
}
fmt.Println(print.NewTreePrinter().Print(c.Root, printables))
case cfg.OutDump:
printer = &print.DumpPrinter{}
printables := make([]print.DumpRepo, len(repos))
for i := range repos {
printables[i] = &repos[i]
}
fmt.Println(print.NewDumpPrinter().Print(printables))
default:
return fmt.Errorf("invalid --out flag; allowed values: [%s]", strings.Join(cfg.AllowedOut, ", "))
}
fmt.Println(printer.Print(c.Root, repos))
return nil
}
func findRepos(root string) ([]string, error) {
repos = []string{}
if _, err := os.Stat(root); err != nil {
return nil, fmt.Errorf("repos root %s doesn't exist or can't be accessed", root)
}
walkOpts := &godirwalk.Options{
ErrorCallback: errorCb,
Callback: walkCb,
// Use Unsorted to improve speed because repos will be processed by goroutines in a random order anyway.
Unsorted: true,
}
err := godirwalk.Walk(root, walkOpts)
if err != nil {
return nil, err
}
if len(repos) == 0 {
return nil, fmt.Errorf("no git repos found in root path %s", root)
}
return repos, nil
}
func walkCb(path string, ent *godirwalk.Dirent) error {
if ent.IsDir() && ent.Name() == ".git" {
repos = append(repos, strings.TrimSuffix(path, ".git"))
return errSkipNode
}
return nil
}
func errorCb(_ string, err error) godirwalk.ErrorAction {
// Skip .git directory and directories we don't have permissions to access
// TODO: Will syscall.EACCES work on windows?
if errors.Is(err, errSkipNode) || errors.Is(err, syscall.EACCES) {
return godirwalk.SkipNode
}
return godirwalk.Halt
}
func openAll(paths []string) ([]*repo.Repo, error) {
var repos []*repo.Repo
reposChan := make(chan *repo.Repo)
for _, path := range paths {
go func(path string) {
repo, err := repo.Open(path)
if err != nil {
// TODO handle error
fmt.Println(err)
}
err = repo.LoadStatus()
if err != nil {
// TODO handle error
fmt.Println(err)
}
// when error happened we just sent a nil
reposChan <- repo
}(path)
}
for repo := range reposChan {
repos = append(repos, repo)
// TODO: is this the right way to close the channel? What if we have non-unique paths?
if len(repos) == len(paths) {
close(reposChan)
}
}
// sort the final array to make printing easier
sort.Slice(repos, func(i, j int) bool {
return strings.Compare(repos[i].Path, repos[j].Path) < 0
})
return repos, nil
}