Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ go mod download
## 아키텍처

### CLI 구조 (Cobra 기반)
- `cmd/root.go` - 루트 명령어 및 전역 플래그 (`--config`, `--verbose`, `--quiet`)
- `cmd/root.go` - 루트 명령어 및 전역 플래그 (`--config`, `--verbose`)
- `cmd/*.go` - 각 서브커맨드 (init, sync, unsync, list, add, version)

### 핵심 패키지 (`internal/gitvolume`)
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | ---------------------------------------------- |
| `--dry-run` | `sync`, `unsync` | Show what would be done without making changes |
| `--relative` | `sync` | Create relative symlinks instead of absolute |
| `--verbose`, `-v` | All | Verbose output |
| `--quiet`, `-q` | All | Suppress non-error output |
| `--verbose`, `-v` | All | Verbosity level: 0=errors only, 1=normal (default), 2=detailed |
| `--config`, `-c` | All | Custom config file path |

## 🛡️ Safety Features
Expand Down
2 changes: 1 addition & 1 deletion cmd/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var globalListCmd = &cobra.Command{
Long: `Displays a tree of all files currently stored in the global git-volume directory.`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{Quiet: quiet})
gv, err := gitvolume.New(commandOptions(false))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/global_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Examples:
Args: cobra.MinimumNArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{Quiet: quiet})
gv, err := gitvolume.New(commandOptions(false))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/global_edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Examples:
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{Quiet: quiet})
gv, err := gitvolume.New(commandOptions(false))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/global_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Examples:
SilenceUsage: true,
Aliases: []string{"rm"},
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{Quiet: quiet})
gv, err := gitvolume.New(commandOptions(false))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var initCmd = &cobra.Command{
git-volume.yaml configuration file in the current directory.`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{Quiet: quiet})
gv, err := gitvolume.New(commandOptions(false))
if err != nil {
return err
}
Expand Down
30 changes: 24 additions & 6 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ Copyright © 2026 laggu
package cmd

import (
"fmt"

"github.com/laggu/git-volume/internal/gitvolume"
"github.com/spf13/cobra"
)

// Global flags
var (
cfgFile string
verbose bool
quiet bool
cfgFile string
verbosity int
)

// rootCmd represents the base command when called without any subcommands
Expand All @@ -22,16 +24,32 @@ var rootCmd = &cobra.Command{
by dynamically mounting them using a git-volume.yaml manifest.

"Keep code in Git, mount environments as volumes."`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if verbosity < gitvolume.VerbosityQuiet || verbosity > gitvolume.VerbosityDetailed {
return fmt.Errorf("invalid --verbose level %d (allowed: 0, 1, 2)", verbosity)
}

return nil
},
}

// Execute adds all child commands to the root command and sets flags appropriately.
func Execute() error {
return rootCmd.Execute()
}

func commandOptions(useConfig bool) gitvolume.Options {
opts := gitvolume.Options{Verbosity: verbosity}
if useConfig {
opts.ConfigPath = cfgFile
}
return opts
}

func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file path (default: auto-detected)")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
rootCmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "suppress non-error output")
rootCmd.MarkFlagsMutuallyExclusive("verbose", "quiet")
rootCmd.PersistentFlags().IntVarP(&verbosity, "verbose", "v", gitvolume.VerbosityNormal, "verbosity level (0=errors only, 1=normal, 2=detailed)")
if flag := rootCmd.PersistentFlags().Lookup("verbose"); flag != nil {
flag.NoOptDefVal = "2"
}
}
5 changes: 1 addition & 4 deletions cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ var statusCmd = &cobra.Command{
Short: "Show the status of all volumes",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{
ConfigPath: cfgFile,
Quiet: quiet,
})
gv, err := gitvolume.New(commandOptions(true))
if err != nil {
return err
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@ It checks the current directory for git-volume.yaml first. If not found,
it looks for it in the main Git worktree (inheritance).`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{
ConfigPath: cfgFile,
Verbose: verbose,
Quiet: quiet,
})
gv, err := gitvolume.New(commandOptions(true))
if err != nil {
return err
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/unsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ For copied files, it verifies that the file content has not changed compared
to the source before deleting. If changed, it skips deletion to prevent data loss.`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
gv, err := gitvolume.New(gitvolume.Options{
ConfigPath: cfgFile,
Verbose: verbose,
Quiet: quiet,
})
gv, err := gitvolume.New(commandOptions(true))
if err != nil {
return err
}
Expand Down
15 changes: 7 additions & 8 deletions docs/specs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ git volume init [flags]

### Flags
- `-h, --help`: help for init
- `-q, --quiet`: suppress all output except errors
- `-v, --verbose int`: verbosity level (0=errors only, 1=normal default, 2=detailed)

### Example
```bash
# Basic initialization
git volume init

# Run quietly
git volume init -q
# Errors only output
git volume init --verbose 0
```

---
Expand All @@ -45,7 +45,7 @@ git volume sync [flags]
### Flags
- `--dry-run`: show what would be done without making actual changes
- `--relative`: create relative symbolic links instead of absolute ones
- `-v, --verbose`: verbose output
- `-v, --verbose int`: verbosity level (0=errors only, 1=normal default, 2=detailed)
- `-c, --config string`: manually specify config file path (default: auto-detected)

### Example
Expand All @@ -72,7 +72,7 @@ git volume unsync [flags]

### Flags
- `--dry-run`: show what would be removed without actually deleting
- `-v, --verbose`: verbose output
- `-v, --verbose int`: verbosity level (0=errors only, 1=normal default, 2=detailed)

### Example
```bash
Expand All @@ -99,7 +99,7 @@ git volume status [flags]

### Flags
- `-c, --config string`: specify configuration file path
- (Note: The `status` command displays detailed information by default, which can be suppressed with the `-q` flag. The `-v` flag has no separate effect.)
- `-v, --verbose int`: verbosity level (0=errors only, 1=normal default, 2=detailed)

### Example
```bash
Expand Down Expand Up @@ -181,5 +181,4 @@ git volume global edit dev.env
Flags available for all commands.

- `-c, --config`: specify config file path
- `-v, --verbose`: verbose output mode
- `-q, --quiet`: suppress output except errors
- `-v, --verbose int`: verbosity level (`0=errors only`, `1=normal`, `2=detailed`)
3 changes: 1 addition & 2 deletions docs/translations/README_de.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | --------------------------------------------------- |
| `--dry-run` | `sync`, `unsync` | Zeigen was getan würde, ohne Änderungen vorzunehmen |
| `--relative` | `sync` | Relative statt absolute symbolische Links erstellen |
| `--verbose`, `-v` | Alle | Ausführliche Ausgabe |
| `--quiet`, `-q` | Alle | Nicht-Fehler-Ausgaben unterdrücken |
| `--verbose`, `-v` | Alle | Ausgabestufe: 0=nur Fehler, 1=normal (Standard), 2=detailliert |
| `--config`, `-c` | Alle | Benutzerdefinierter Konfigurationsdateipfad |

## 🛡️ Sicherheitsfunktionen
Expand Down
3 changes: 1 addition & 2 deletions docs/translations/README_es.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | ------------------------------------------------------ |
| `--dry-run` | `sync`, `unsync` | Mostrar qué se haría sin realizar cambios |
| `--relative` | `sync` | Crear enlaces simbólicos relativos en vez de absolutos |
| `--verbose`, `-v` | Todos | Salida detallada |
| `--quiet`, `-q` | Todos | Ocultar salida no relacionada con errores |
| `--verbose`, `-v` | Todos | Nivel de salida: 0=solo errores, 1=normal (por defecto), 2=detallado |
| `--config`, `-c` | Todos | Ruta personalizada del archivo de configuración |

## 🛡️ Características de seguridad
Expand Down
3 changes: 1 addition & 2 deletions docs/translations/README_fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | --------------------------------------------------------- |
| `--dry-run` | `sync`, `unsync` | Afficher ce qui serait fait sans effectuer de changements |
| `--relative` | `sync` | Créer des liens symboliques relatifs au lieu d'absolus |
| `--verbose`, `-v` | Toutes | Sortie détaillée |
| `--quiet`, `-q` | Toutes | Masquer les sorties non liées aux erreurs |
| `--verbose`, `-v` | Toutes | Niveau de sortie : 0=erreurs seulement, 1=normal (par défaut), 2=détaillé |
| `--config`, `-c` | Toutes | Chemin personnalisé du fichier de configuration |

## 🛡️ Fonctionnalités de sécurité
Expand Down
3 changes: 1 addition & 2 deletions docs/translations/README_it.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | ------------------------------------------------------ |
| `--dry-run` | `sync`, `unsync` | Mostrare cosa verrebbe fatto senza apportare modifiche |
| `--relative` | `sync` | Creare link simbolici relativi invece di assoluti |
| `--verbose`, `-v` | Tutti | Output dettagliato |
| `--quiet`, `-q` | Tutti | Nascondere l'output non correlato agli errori |
| `--verbose`, `-v` | Tutti | Livello output: 0=solo errori, 1=normale (predefinito), 2=dettagliato |
| `--config`, `-c` | Tutti | Percorso personalizzato del file di configurazione |

## 🛡️ Funzionalità di sicurezza
Expand Down
3 changes: 1 addition & 2 deletions docs/translations/README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | ---------------------------------------- |
| `--dry-run` | `sync`, `unsync` | 実際の変更なしに実行内容を表示 |
| `--relative` | `sync` | 絶対パスの代わりに相対パスのリンクを作成 |
| `--verbose`, `-v` | 全て | 詳細出力 |
| `--quiet`, `-q` | 全て | エラー以外の出力を非表示 |
| `--verbose`, `-v` | 全て | 出力レベル: 0=エラーのみ、1=通常(既定)、2=詳細 |
| `--config`, `-c` | 全て | 設定ファイルパスの指定 |

## 🛡️ 安全機能
Expand Down
3 changes: 1 addition & 2 deletions docs/translations/README_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | ----------------------------------------- |
| `--dry-run` | `sync`, `unsync` | 실제 변경 없이 수행할 작업만 표시 |
| `--relative` | `sync` | 절대 경로 대신 상대 경로 심볼릭 링크 생성 |
| `--verbose`, `-v` | 전체 | 상세 출력 |
| `--quiet`, `-q` | 전체 | 에러 외 출력 숨김 |
| `--verbose`, `-v` | 전체 | 출력 레벨: 0=에러만, 1=기본(기본값), 2=상세 |
| `--config`, `-c` | 전체 | 설정 파일 경로 지정 |

## 🛡️ 안전 장치
Expand Down
3 changes: 1 addition & 2 deletions docs/translations/README_pt.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | ---------------------------------------------------- |
| `--dry-run` | `sync`, `unsync` | Mostrar o que seria feito sem realizar alterações |
| `--relative` | `sync` | Criar links simbólicos relativos em vez de absolutos |
| `--verbose`, `-v` | Todos | Saída detalhada |
| `--quiet`, `-q` | Todos | Ocultar saída não relacionada a erros |
| `--verbose`, `-v` | Todos | Nível de saída: 0=somente erros, 1=normal (padrão), 2=detalhado |
| `--config`, `-c` | Todos | Caminho personalizado do arquivo de configuração |

## 🛡️ Recursos de segurança
Expand Down
3 changes: 1 addition & 2 deletions docs/translations/README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ volumes:
| ----------------- | ---------------- | -------------------------------- |
| `--dry-run` | `sync`, `unsync` | 不做实际更改,仅显示将执行的操作 |
| `--relative` | `sync` | 创建相对路径符号链接而非绝对路径 |
| `--verbose`, `-v` | 全部 | 详细输出 |
| `--quiet`, `-q` | 全部 | 隐藏非错误输出 |
| `--verbose`, `-v` | 全部 | 输出级别:0=仅错误,1=普通(默认),2=详细 |
| `--config`, `-c` | 全部 | 指定配置文件路径 |

## 🛡️ 安全功能
Expand Down
10 changes: 5 additions & 5 deletions internal/gitvolume/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ func (g *GitVolume) beforeAllAdd(files []string, opts AddOptions) error {
}

func (g *GitVolume) afterAllAdd(errs []error) error {
if len(errs) > 0 && !g.quiet {
fmt.Printf("❌ Global add completed with %d error(s)\n", len(errs))
if len(errs) > 0 && g.isNormalOrHigher() {
fmt.Fprintf(os.Stderr, "❌ Global add completed with %d error(s)\n", len(errs))
}
return errors.Join(errs...)
}
Expand Down Expand Up @@ -151,14 +151,14 @@ func (g *GitVolume) add(file string, prepared addPrepared, opts AddOptions) erro

func (g *GitVolume) afterAdd(file, dstPath string, opts AddOptions, err error, errs *[]error) {
if err != nil {
if !g.quiet {
fmt.Printf("❌ Failed to add %s: %v\n", file, err)
if g.isNormalOrHigher() {
fmt.Fprintf(os.Stderr, "❌ Failed to add %s: %v\n", file, err)
}
*errs = append(*errs, err)
return
}

if !g.quiet {
if g.isDetailed() {
relPath, err := filepath.Rel(g.ctx.GlobalDir, dstPath)
if err != nil {
// This should not happen due to prior validation, but as a fallback:
Expand Down
2 changes: 1 addition & 1 deletion internal/gitvolume/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func TestGlobalAdd(t *testing.T) {
ctx: &Context{
GlobalDir: globalDir,
},
quiet: true,
verbosity: VerbosityQuiet,
}

// Create test files
Expand Down
9 changes: 4 additions & 5 deletions internal/gitvolume/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,7 @@ func NewContext() (*Context, error) {

// Load loads configuration from config file and populates the Context.
// configPath: custom config file path (empty string for auto-detection)
// quiet: suppress warning messages
func (c *Context) Load(configPath string, quiet bool) error {
func (c *Context) Load(configPath string, verbosity int) error {
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %w", err)
Expand All @@ -358,7 +357,7 @@ func (c *Context) Load(configPath string, quiet bool) error {
}

// 3. Load and apply config
cfg, err := loadConfig(absConfigPath, quiet)
cfg, err := loadConfig(absConfigPath, verbosity)
if err != nil {
return err
}
Expand Down Expand Up @@ -433,7 +432,7 @@ type rawConfig struct {

// loadConfig reads and parses the configuration file
// Returns rawConfig and error
func loadConfig(path string, quiet bool) (*rawConfig, error) {
func loadConfig(path string, verbosity int) (*rawConfig, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
Expand All @@ -445,7 +444,7 @@ func loadConfig(path string, quiet bool) (*rawConfig, error) {
}

// Warn if no volumes defined
if len(cfg.Volumes) == 0 && !quiet {
if len(cfg.Volumes) == 0 && verbosity >= VerbosityNormal {
fmt.Fprintf(os.Stderr, "⚠️ Warning: no volumes defined in %s\n", path)
}

Expand Down
Loading