Adds initial gitlocal CLI and core functionality
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`.
This commit is contained in:
94
internal/git/git.go
Normal file
94
internal/git/git.go
Normal file
@@ -0,0 +1,94 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user