diff --git a/.golangci.yml b/.golangci.yml index 86de6f4..f930ed8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -15,6 +15,7 @@ linters: disable: - depguard # We don't have any packages we need to block - paralleltest # Tests are fast already and paralellizing them adds complexity + - wsl # We use wsl_v5 instead exclusions: rules: diff --git a/cmd/get.go b/cmd/get.go index d44926d..8fba2d5 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -62,6 +62,7 @@ func runGetCommand(cmd *cobra.Command, args []string) error { Root: viper.GetString(cfg.KeyReposRoot), URL: url, } + return pkg.Get(config) } diff --git a/cmd/main.go b/cmd/main.go index 84126ba..22b5bd6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -33,6 +33,7 @@ func handleGitGetInvocation() (string, []string) { if len(os.Args) > 1 && (os.Args[1] == "get" || os.Args[1] == "list") { return os.Args[1], os.Args[2:] } + return "get", os.Args[1:] } @@ -44,6 +45,7 @@ func handleDefaultInvocation() (string, []string) { if len(os.Args) > 1 { return os.Args[1], os.Args[2:] } + return "get", []string{} } diff --git a/cmd/main_test.go b/cmd/main_test.go index 124b54c..2570ec0 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -83,6 +83,7 @@ func TestDetermineCommand(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Save original os.Args oldArgs := os.Args + defer func() { os.Args = oldArgs }() // Set test args @@ -144,6 +145,7 @@ func TestHandleGitGetInvocation(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Save original os.Args oldArgs := os.Args + defer func() { os.Args = oldArgs }() // Set test args @@ -187,6 +189,7 @@ func TestHandleGitListInvocation(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Save original os.Args oldArgs := os.Args + defer func() { os.Args = oldArgs }() // Set test args @@ -236,6 +239,7 @@ func TestHandleDefaultInvocation(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Save original os.Args oldArgs := os.Args + defer func() { os.Args = oldArgs }() // Set test args @@ -252,4 +256,4 @@ func TestHandleDefaultInvocation(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/pkg/cfg/config_test.go b/pkg/cfg/config_test.go index 215e1ab..36d06db 100644 --- a/pkg/cfg/config_test.go +++ b/pkg/cfg/config_test.go @@ -96,7 +96,6 @@ func testConfigOnlyInGitconfig(t *testing.T) { func testConfigOnlyInEnvVar(t *testing.T) { Init(&gitconfigEmpty{}) os.Setenv(envVarName, fromEnv) - } func testConfigInGitconfigAndEnvVar(t *testing.T) { diff --git a/pkg/dump.go b/pkg/dump.go index ecb923f..decc2e0 100644 --- a/pkg/dump.go +++ b/pkg/dump.go @@ -28,10 +28,14 @@ func parseDumpFile(path string) ([]parsedLine, error) { scanner := bufio.NewScanner(file) - var parsedLines []parsedLine - var line int + var ( + parsedLines []parsedLine + line int + ) + for scanner.Scan() { line++ + parsed, err := parseLine(scanner.Text()) if err != nil && !errors.Is(errEmptyLine, err) { return nil, fmt.Errorf("failed parsing dump file line %d: %w", line, err) diff --git a/pkg/get.go b/pkg/get.go index 407fff8..754c73b 100644 --- a/pkg/get.go +++ b/pkg/get.go @@ -30,6 +30,7 @@ func Get(c *GetCfg) error { if c.Dump != "" { return cloneDumpFile(c) } + return nil } @@ -74,10 +75,12 @@ func cloneDumpFile(c *GetCfg) error { } fmt.Printf("Cloning %s...\n", opts.URL.String()) + _, err = git.Clone(opts) if err != nil { return err } } + return nil } diff --git a/pkg/git/finder.go b/pkg/git/finder.go index b9fd39f..5ccd96d 100644 --- a/pkg/git/finder.go +++ b/pkg/git/finder.go @@ -18,7 +18,6 @@ var errDirNotExist = fmt.Errorf("directory doesn't exist") // Exists returns true if a directory exists. If it doesn't or the directory can't be accessed it returns an error. func Exists(path string) (bool, error) { _, err := os.Stat(path) - if err == nil { return true, nil } @@ -61,6 +60,7 @@ func (f *RepoFinder) Find() error { if os.IsPermission(err) { return nil // Skip this path but continue } + return fmt.Errorf("failed to walk %s: %w", path, err) } @@ -73,6 +73,7 @@ func (f *RepoFinder) Find() error { if d.Name() == dotgit { parentPath := filepath.Dir(path) f.addIfOk(parentPath) + return fs.SkipDir // Skip the .git directory contents } @@ -85,7 +86,6 @@ func (f *RepoFinder) Find() error { return nil // Continue walking }) - if err != nil { return fmt.Errorf("failed to walk directory tree: %w", err) } diff --git a/pkg/git/repo.go b/pkg/git/repo.go index 86161e5..31e86f7 100644 --- a/pkg/git/repo.go +++ b/pkg/git/repo.go @@ -61,6 +61,7 @@ func Clone(opts *CloneOpts) (*Repo, error) { } Repo, err := Open(opts.Path) + return Repo, err } @@ -79,6 +80,7 @@ func (r *Repo) Uncommitted() (int, error) { } count := 0 + for _, line := range out { // Don't count lines with untracked files and empty lines. if !strings.HasPrefix(line, untracked) && strings.TrimSpace(line) != "" { @@ -97,6 +99,7 @@ func (r *Repo) Untracked() (int, error) { } count := 0 + for _, line := range out { if strings.HasPrefix(line, untracked) { count++ @@ -122,6 +125,7 @@ func (r *Repo) CurrentBranch() (string, error) { // Fall back to "main" as the modern default return "main", nil } + return "", err } @@ -190,6 +194,7 @@ func (r *Repo) Remote() (string, error) { if strings.Contains(err.Error(), "No remote configured to list refs from") { return "", nil // Return empty string instead of error for missing remote } + return "", err } diff --git a/pkg/git/repo_test.go b/pkg/git/repo_test.go index 0897305..baf2d31 100644 --- a/pkg/git/repo_test.go +++ b/pkg/git/repo_test.go @@ -47,8 +47,8 @@ func TestUncommitted(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { r, _ := Open(test.repoMaker(t).Path()) - got, err := r.Uncommitted() + got, err := r.Uncommitted() if err != nil { t.Errorf("got error %q", err) } @@ -95,8 +95,8 @@ func TestUntracked(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { r, _ := Open(test.repoMaker(t).Path()) - got, err := r.Uncommitted() + got, err := r.Uncommitted() if err != nil { t.Errorf("got error %q", err) } @@ -139,8 +139,8 @@ func TestCurrentBranch(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { r, _ := Open(test.repoMaker(t).Path()) - got, err := r.CurrentBranch() + got, err := r.CurrentBranch() if err != nil { t.Errorf("got error %q", err) } @@ -182,8 +182,8 @@ func TestBranches(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { r, _ := Open(test.repoMaker(t).Path()) - got, err := r.Branches() + got, err := r.Branches() if err != nil { t.Errorf("got error %q", err) } @@ -287,6 +287,7 @@ func TestAheadBehind(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { r, _ := Open(test.repoMaker(t).Path()) + upstream, err := r.Upstream(test.branch) if err != nil { t.Errorf("got error %q", err) @@ -313,7 +314,6 @@ func TestCleanupFailedClone(t *testing.T) { // └── x/ // └── y/ // └── file.txt - tests := []struct { path string // path to cleanup wantGone string // this path should be deleted, if empty - nothing should be deleted @@ -393,6 +393,7 @@ func TestRemote(t *testing.T) { if test.wantErr && err == nil { t.Errorf("expected error but got none") } + if !test.wantErr && err != nil { t.Errorf("unexpected error: %q", err) } @@ -413,8 +414,8 @@ func createTestDirTree(t *testing.T) string { root := test.TempDir(t, "") err := os.MkdirAll(filepath.Join(root, "a", "b", "c"), os.ModePerm) err = os.MkdirAll(filepath.Join(root, "a", "x", "y"), os.ModePerm) - _, err = os.Create(filepath.Join(root, "a", "x", "y", "file.txt")) + _, err = os.Create(filepath.Join(root, "a", "x", "y", "file.txt")) if err != nil { t.Fatal(err) } diff --git a/pkg/git/status.go b/pkg/git/status.go index 6825c46..6f5607c 100644 --- a/pkg/git/status.go +++ b/pkg/git/status.go @@ -32,12 +32,14 @@ func (r *Repo) LoadStatus(fetch bool) *Status { } var err error + status.current, err = r.CurrentBranch() if err != nil { status.errors = append(status.errors, err.Error()) } var errs []error + status.branches, errs = r.loadBranches() for _, err := range errs { status.errors = append(status.errors, err.Error()) @@ -69,6 +71,7 @@ func (r *Repo) loadBranches() (map[string]string, []error) { for _, branch := range branches { status, err := r.loadBranchStatus(branch) statuses[branch] = status + if err != nil { errors = append(errors, err) } @@ -100,6 +103,7 @@ func (r *Repo) loadBranchStatus(branch string) (string, error) { if ahead != 0 { res = append(res, fmt.Sprintf("%d ahead", ahead)) } + if behind != 0 { res = append(res, fmt.Sprintf("%d behind", behind)) } @@ -126,6 +130,7 @@ func (r *Repo) loadWorkTree() (string, error) { if uncommitted != 0 { res = append(res, fmt.Sprintf("%d uncommitted", uncommitted)) } + if untracked != 0 { res = append(res, fmt.Sprintf("%d untracked", untracked)) } @@ -151,6 +156,7 @@ func (s *Status) Branches() []string { branches = append(branches, b) } } + return branches } diff --git a/pkg/git/test/testrepos.go b/pkg/git/test/testrepos.go index 2c65427..407af6f 100644 --- a/pkg/git/test/testrepos.go +++ b/pkg/git/test/testrepos.go @@ -30,6 +30,7 @@ func RepoEmptyInDir(t *testing.T, parent string) *Repo { } r.init() + return r } @@ -97,6 +98,7 @@ func RepoWithBranchWithUpstream(t *testing.T) *Repo { r := origin.clone() r.checkout("feature/branch") + return r } @@ -107,6 +109,7 @@ func RepoWithBranchWithoutUpstream(t *testing.T) *Repo { r := origin.clone() r.branch("feature/branch") r.checkout("feature/branch") + return r } diff --git a/pkg/list.go b/pkg/list.go index 3550026..8b7bd9d 100644 --- a/pkg/list.go +++ b/pkg/list.go @@ -23,6 +23,7 @@ func List(c *ListCfg) error { } statuses := finder.LoadAll(c.Fetch) + printables := make([]print.Printable, len(statuses)) for i := range statuses { printables[i] = statuses[i] diff --git a/pkg/print/tree.go b/pkg/print/tree.go index fc327a3..16fd97b 100644 --- a/pkg/print/tree.go +++ b/pkg/print/tree.go @@ -46,6 +46,7 @@ func Root(val string) *Node { root := &Node{ val: val, } + return root } @@ -61,6 +62,7 @@ func (n *Node) Add(val string) *Node { depth: n.depth + 1, } n.children = append(n.children, child) + return child } @@ -108,9 +110,11 @@ func buildTree(root string, repos []Printable) *Node { continue } + node = child } } + return tree } @@ -180,8 +184,11 @@ func indentation(node *Node) string { var indent strings.Builder - const space = " " - const link = "│ " + const ( + space = " " + link = "│ " + ) + for _, y := range levels { if y { indent.WriteString(space) @@ -203,12 +210,15 @@ func (n *Node) isYoungest() bool { } sisters := n.parent.children + var myIndex int + for i, sis := range sisters { if sis.val == n.val { myIndex = i break } } + return myIndex == len(sisters)-1 } diff --git a/pkg/run/run.go b/pkg/run/run.go index 4b8216b..20e8148 100644 --- a/pkg/run/run.go +++ b/pkg/run/run.go @@ -78,6 +78,7 @@ func (c *Cmd) AndCaptureLine() (string, error) { if err != nil { return "", err } + return lines[0], nil } @@ -90,6 +91,7 @@ func (c *Cmd) AndShow() error { if err != nil { return &GitError{&bytes.Buffer{}, c.args, c.path, err} } + return nil } @@ -104,6 +106,7 @@ func (c *Cmd) AndShutUp() error { if err != nil { return &GitError{errStream, c.args, c.path, err} } + return nil } @@ -123,7 +126,6 @@ func (e GitError) Error() string { } return fmt.Sprintf("git %s failed on %s: %s", e.Args, e.Path, msg) - } func lines(output []byte) []string { diff --git a/pkg/url.go b/pkg/url.go index 941db48..d383bed 100644 --- a/pkg/url.go +++ b/pkg/url.go @@ -29,6 +29,7 @@ func ParseURL(rawURL string, defaultHost string, defaultScheme string) (url *url } normalizeURL(url, defaultHost, defaultScheme) + return url, nil } @@ -49,6 +50,7 @@ func parseRawURL(rawURL string) (*urlpkg.URL, error) { if err != nil { return nil, fmt.Errorf("failed parsing URL %s: %w", rawURL, err) } + return url, nil }