Thank you for your interest in contributing to this repository! Bug reports, feature requests, and pull requests (PRs) are all welcome.
- Please use Issues for bug reports, feature requests, or questions.
- Include as much detail as possible: steps to reproduce, expected/actual behavior, environment info (OS, Go version, etc.).
- Do not commit directly to the
mainbranch. Always create a new branch and submit a PR. - Each PR should focus on a single purpose (e.g., separate bug fixes and new features).
- Run
make fmtandmake lintandmake testbefore submitting, and ensure there are no errors. - Please manually test major commands as well.
- Go 1.25 or later, standard library only
- Pass linting (golangci-lint) and static analysis
When implementing new features or modifying existing ones, please ensure to:
When adding new commands or changing command names/syntax, update the centralized registry and any surfaces that depend on it.
- cmd/command/registry.go: Add or modify
CommandInfoentries (usage, examples, handler IDs, visibility)- Set
Hidden: truefor experimental/internal commands you do not want exposed viahelpor interactive search.
- Set
- Auto-generated: Run
make docsto update the README.md command table from the registry
- Auto-generated: Run
make docs(ormake completions) to regenerate the Bash/Zsh/Fish completion scripts from the registry. - Do not edit files under
cmd/completions/manually—changes will be overwritten by the generator.
📋 Checklist for Command Changes:
- cmd/command/registry.go entry added/updated (usage, examples, handler)
- Run
make docsto update README.md and regenerate shell completions - Refresh demo GIFs (
make demos) if command output or interactions changed - All tests pass (
make test) - No lint errors (
make lint)
ggc uses a centralized command registry system that eliminates the need to update multiple files when adding commands. Here's the streamlined workflow:
Edit cmd/command/registry.go and add your command entry:
{
Name: "mycommand",
Category: command.CategoryUtility,
Summary: "Does something useful",
Usage: []string{"ggc mycommand", "ggc mycommand --help"},
Examples: []string{"ggc mycommand", "ggc mycommand file.txt"},
HandlerID: "mycommand",
Subcommands: []command.SubcommandInfo{
{
Name: "mycommand subaction",
Summary: "Performs a sub-action",
Usage: []string{"ggc mycommand subaction"},
},
},
},Add your handler function to the appropriate cmd/*.go file and register it in cmd/cmd.go:
// In cmd/cmd.go handlers map
"mycommand": func(args []string) { cmd.MyCommand(args) },make docs # Updates README.md command table automatically- Place command implementations in appropriate files under
cmd/ - Add corresponding test files
- Use consistent error handling and output formatting
- Provide clear, helpful error messages
- Add examples in help text
- Ensure command behavior is intuitive
The animated demos in docs/demos/generated are recorded with VHS. Install vhs, ttyd, and ffmpeg, then run make demos (or make docs) whenever a change affects the showcased workflows. Temporary git fixtures are created automatically under docs/demos/workspaces/ and wiped during each run.
- macOS/Linux/WSL2 are recommended environments
- Use
make buildto build the binary,make testto run tests - Update tests when adding or modifying features
- Add test cases for error scenarios
To reduce mock surface area and improve maintainability, the git package defines small, focused interfaces that represent cohesive slices of functionality. For example:
// internal/git/*.go
type DiffReader interface {
Diff() (string, error)
DiffStaged() (string, error)
DiffHead() (string, error)
DiffWith(args []string) (string, error)
}
Guidelines:
- Commands in
cmd/should depend on the smallest interface they need (e.g.,git.DiffReaderforcmd/diff.go). - The concrete
git.Clientininternal/git/implements the full set of operations and automatically satisfies these smaller interfaces. - In tests, prefer defining minimal mocks that satisfy only the required small interface instead of a large, catch‑all client surface.
This approach follows the Interface Segregation Principle and helps avoid updating large mock types when unrelated functionality changes.
- Prefer narrow, role-based interfaces over a single large one.
- Use clear suffixes to communicate intent:
Readerfor read-only queries (e.g.,DiffReader,StatusReader,BranchReader).Writerfor mutating operations (e.g.,CommitWriter,BranchWriter).- Use simple nouns for single-purpose operations (e.g.,
Pusher,Puller).
- Compose small interfaces for command needs instead of expanding them:
- Example composite used by status:
type StatusInfoReader interface {
StatusReader
BranchUpstreamReader
}
- Define interfaces in
internal/git/*.go; implement behavior ongit.Clientin the same package. - Constructors in
cmd/*should accept the smallest interface required, for example:
// cmd/diff.go
type Differ struct { gitClient git.DiffReader /* ... */ }
func NewDiffer(c git.DiffReader) *Differ { /* ... */ }
// cmd/commit.go
type Committer struct { gitClient git.CommitWriter /* ... */ }
func NewCommitter(c git.CommitWriter) *Committer { /* ... */ }
- Tests should create minimal mocks that satisfy only the specific interface required by the command being tested.
ggc is a CLI tool, not a library. All implementation packages live under internal/ and are not exported to external consumers:
internal/git/— Git operation wrappers and interface typesinternal/config/— Configuration loading, validation, and keybindingsinternal/interactive/— TUI rendering, state machine, and keybinding dispatchinternal/keybindings/— Keybinding profiles (default, vi, emacs, readline)internal/prompt/— Input prompt utilitiesinternal/templates/— Help message templatesinternal/termio/— Terminal I/O abstractioninternal/testutil/— Shared test utilities (mock git client)internal/ui/— UI model types
Do not add packages under pkg/; use internal/ for all new packages.
- Struct Names: Use
-ersuffix consistently (e.g.,Brancher,Committer) - Field Names: Match struct names in lowercase (e.g.,
brancher,committer) - Function Names: Use descriptive verbs (e.g.,
GetCurrentBranch,ListLocalBranches)
- Format:
ggc <command> <subcommand> [modifier] [arguments] - No Option Flags: Use subcommands instead of
-flagor--flag - No Hyphens: Use spaces to separate words (e.g.,
clean interactive) - Hierarchical: Group related functionality under common commands
- ✅
ggc restore staged <file> - ✅
ggc commit allow empty - ✅
ggc branch set upstream <branch> <upstream> - ❌
ggc restore --staged <file> - ❌
ggc commit --allow-empty - ❌
ggc branch set-upstream <branch> <upstream>
- Follow the established hierarchy
- Use descriptive subcommand names
- Update all related files:
- Command implementation
- Help templates
- Interactive mode commands
- Completion scripts
- Documentation
- If unsure, please open an Issue for discussion first!
- Documentation and README improvements are also welcome.
Thank you for your cooperation!