Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Loading