Introduces the `gitlocal` command-line tool for managing nested Git repositories. Includes the following main commands: - `convert`: Renames `.git` to `.gitlocal`, allowing a parent repository to ignore the converted child repository. Supports recursive scanning and dry-run options. Tracks converted repositories in a global configuration. - `revert`: Restores `.gitlocal` to `.git`. Includes an option to revert all tracked repositories. - `status`: Displays a list of all repositories currently tracked by `gitlocal`, showing their path, conversion time, and original remote/branch. Establishes internal modules for Git operations, configuration management, and recursive repository scanning. Adds a comprehensive test suite covering core command logic and utility functions. Initializes Go module and basic project `.gitignore`.
95 lines
2.1 KiB
Go
95 lines
2.1 KiB
Go
package git
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
GitDir = ".git"
|
|
GitLocalDir = ".gitlocal"
|
|
)
|
|
|
|
// IsGitRepo checks if the path contains a .git directory
|
|
func IsGitRepo(path string) bool {
|
|
gitPath := filepath.Join(path, GitDir)
|
|
info, err := os.Stat(gitPath)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return info.IsDir()
|
|
}
|
|
|
|
// IsGitLocalRepo checks if the path contains a .gitlocal directory
|
|
func IsGitLocalRepo(path string) bool {
|
|
gitLocalPath := filepath.Join(path, GitLocalDir)
|
|
info, err := os.Stat(gitLocalPath)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return info.IsDir()
|
|
}
|
|
|
|
// GetRemoteURL returns the remote URL for the git repo
|
|
func GetRemoteURL(gitDir string) string {
|
|
cmd := exec.Command("git", "--git-dir", gitDir, "remote", "get-url", "origin")
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(string(output))
|
|
}
|
|
|
|
// GetCurrentBranch returns the current branch for the git repo
|
|
func GetCurrentBranch(gitDir string) string {
|
|
cmd := exec.Command("git", "--git-dir", gitDir, "branch", "--show-current")
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(string(output))
|
|
}
|
|
|
|
// Convert renames .git to .gitlocal
|
|
func Convert(path string) error {
|
|
gitPath := filepath.Join(path, GitDir)
|
|
gitLocalPath := filepath.Join(path, GitLocalDir)
|
|
|
|
if !IsGitRepo(path) {
|
|
return fmt.Errorf("no .git directory found at %s", path)
|
|
}
|
|
|
|
if IsGitLocalRepo(path) {
|
|
return fmt.Errorf(".gitlocal already exists at %s", path)
|
|
}
|
|
|
|
if err := os.Rename(gitPath, gitLocalPath); err != nil {
|
|
return fmt.Errorf("failed to rename .git to .gitlocal: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Revert renames .gitlocal back to .git
|
|
func Revert(path string) error {
|
|
gitPath := filepath.Join(path, GitDir)
|
|
gitLocalPath := filepath.Join(path, GitLocalDir)
|
|
|
|
if !IsGitLocalRepo(path) {
|
|
return fmt.Errorf("no .gitlocal directory found at %s", path)
|
|
}
|
|
|
|
if IsGitRepo(path) {
|
|
return fmt.Errorf(".git already exists at %s", path)
|
|
}
|
|
|
|
if err := os.Rename(gitLocalPath, gitPath); err != nil {
|
|
return fmt.Errorf("failed to rename .gitlocal to .git: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|