Files
gitlocal/internal/config/config.go
Daniel Tomlinson b5f1495680 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`.
2026-04-11 14:48:01 +01:00

112 lines
2.3 KiB
Go

package config
import (
"fmt"
"os"
"path/filepath"
"time"
"gopkg.in/yaml.v3"
)
const (
ConfigVersion = "1"
ConfigFile = ".gitlocal.yml"
)
type Repo struct {
Path string `yaml:"path"`
ConvertedAt time.Time `yaml:"converted_at"`
OriginalRemote string `yaml:"original_remote,omitempty"`
OriginalBranch string `yaml:"original_branch,omitempty"`
}
type Config struct {
Version string `yaml:"version"`
Repos []Repo `yaml:"repos"`
}
// GetConfigPath returns the absolute path to the config file
func GetConfigPath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to get home directory: %w", err)
}
return filepath.Join(home, ConfigFile), nil
}
// Load reads the config file or creates a new empty config if it doesn't exist
func Load() (*Config, error) {
configPath, err := GetConfigPath()
if err != nil {
return nil, err
}
// If config doesn't exist, return empty config
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return &Config{
Version: ConfigVersion,
Repos: []Repo{},
}, nil
}
data, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
return &cfg, nil
}
// Save writes the config to disk
func (c *Config) Save() error {
configPath, err := GetConfigPath()
if err != nil {
return err
}
data, err := yaml.Marshal(c)
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.WriteFile(configPath, data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
return nil
}
// AddRepo adds a new repo to the config
func (c *Config) AddRepo(repo Repo) {
// Remove existing entry if it exists
c.RemoveRepo(repo.Path)
c.Repos = append(c.Repos, repo)
}
// RemoveRepo removes a repo from the config by path
func (c *Config) RemoveRepo(path string) {
filtered := []Repo{}
for _, r := range c.Repos {
if r.Path != path {
filtered = append(filtered, r)
}
}
c.Repos = filtered
}
// FindRepo returns a repo by path, or nil if not found
func (c *Config) FindRepo(path string) *Repo {
for _, r := range c.Repos {
if r.Path == path {
return &r
}
}
return nil
}