Skip to content

Commit 9d00d9f

Browse files
authored
docs: stop duplicating the command table and quick-start in README (#403)
The README and the docs site were both carrying the same content: * The 100-row **Available Commands** table lived in README.md *and* in `docs/guide/commands.md` (auto-generated with far more detail: summary / aliases / usage / subcommands / examples per command). * The **Quick start** snippet and **Unified syntax** note lived in README.md *and* in `docs/guide/quickstart.md`. * The README **Features** bullets restated what `docs/index.md` already says under "Why ggc?". * The **References** heading was accidentally present twice after the last slim pass. Trim the README to the things only the repo front page needs to do: badges, demo GIFs, a one-paragraph overview, an install snippet, and links into the docs site for everything else. Drop the duplicated feature bullets, command table, quick-start block, and unified-syntax note; they're all a click away. Rewire the generator to match: * `tools/cmd/gendocs/main.go` no longer touches `README.md`. It only emits `docs/guide/commands.md`. All the README-scanning helpers (findCommandSection, findCommandTable, writeUpdatedREADME, etc.) go away. * `Makefile`'s `docs` target updates its log line accordingly. Net: README 218 -> 88 lines; gendocs 300 -> ~130 lines; nothing the docs site already covers is lost.
1 parent 4b2ff52 commit 9d00d9f

3 files changed

Lines changed: 13 additions & 303 deletions

File tree

Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,8 @@ test-and-lint: test lint
7575
.PHONY: docs completions
7676

7777
docs:
78-
@echo "Updating README.md command table..."
78+
@echo "Regenerating docs/guide/commands.md from registry..."
7979
@go run tools/cmd/gendocs/main.go
80-
@echo "README.md command table updated from registry"
8180
@$(MAKE) completions
8281
@echo "Documentation, completions updated successfully"
8382

README.md

Lines changed: 8 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,16 @@ Click any GIF to view full size.
3232

3333
ggc is a Git tool written in Go, offering both a traditional CLI and an interactive TUI with incremental search and multi-command workflows. Run `ggc <subcommand>` directly, or type `ggc` on its own to open the fuzzy picker.
3434

35-
Full docs: **<https://bmf-san.github.io/ggc/>**
35+
Supported: macOS (amd64 / arm64 / universal), Linux (amd64 / arm64), Windows (amd64). Requires Git and Go 1.25+ to build.
3636

37-
## Features
37+
Full documentation lives at **<https://bmf-san.github.io/ggc/>**:
3838

39-
- **Flagless CLI** — every command is verb + words (`ggc branch delete merged`, `ggc commit amend no-edit`). No `-m`/`--flag` juggling.
40-
- **Interactive mode** — fuzzy-search every command, pipe commands into workflows with <kbd>Tab</kbd>, and run the pipeline with <kbd>Ctrl</kbd>+<kbd>T</kbd>.
41-
- **Pickers when arguments are omitted**`ggc branch checkout`, `ggc stash pop`, `ggc restore` all prompt for the target.
42-
- **Composite helpers**`ggc pull rebase`, `ggc push force`, `ggc rebase autosquash`, `ggc fetch prune`, and more.
43-
- **User aliases** — define simple or multi-step aliases in `~/.config/ggc/config.yaml`.
44-
- **Customizable keybindings** — 4 built-in profiles (default, emacs, vi, readline) plus per-OS / per-terminal / per-context overrides.
45-
- **Shell completion** — pre-built scripts for Bash, Zsh, and Fish.
46-
- **Supported:** macOS (amd64 / arm64 / universal), Linux (amd64 / arm64), Windows (amd64). Requires Git and Go 1.25+ to build.
39+
- [Why ggc? + feature highlights](https://bmf-san.github.io/ggc/#why-ggc)
40+
- [Quick start](https://bmf-san.github.io/ggc/guide/quickstart/)
41+
- [Command reference](https://bmf-san.github.io/ggc/guide/commands/) — auto-generated from the registry
42+
- [Interactive mode & workflows](https://bmf-san.github.io/ggc/guide/interactive/)
43+
- [Configuration, aliases, keybindings](https://bmf-san.github.io/ggc/guide/config/)
44+
- [Troubleshooting](https://bmf-san.github.io/ggc/guide/troubleshooting/)
4745

4846
## Install
4947

@@ -60,135 +58,6 @@ go install github.com/bmf-san/ggc/v8@latest
6058

6159
Windows binaries, pre-built archives, and source builds are covered in the [installation guide](https://bmf-san.github.io/ggc/guide/install/). After installing, run `ggc doctor` to verify.
6260

63-
## Quick start
64-
65-
```bash
66-
ggc status # working tree status
67-
ggc add . # stage everything
68-
ggc commit "fix: off-by-one" # no -m required
69-
ggc log graph # prettier git log
70-
ggc branch checkout # list + pick a local branch
71-
ggc rebase interactive # interactive rebase
72-
```
73-
74-
Run `ggc` with no arguments to enter interactive mode. See the [quick start](https://bmf-san.github.io/ggc/guide/quickstart/) and [interactive mode guide](https://bmf-san.github.io/ggc/guide/interactive/) for more.
75-
76-
### Unified syntax and `--` separator
77-
78-
ggc uses a flagless, space-separated syntax. To pass a literal that starts with `-`, use the standard `--` separator:
79-
80-
```bash
81-
ggc commit -- - fix leading dash
82-
```
83-
84-
Everything after `--` is treated as data, never as subcommands.
85-
86-
## Command reference
87-
88-
### Available Commands
89-
90-
| Command | Description |
91-
|--------|-------------|
92-
| `add .` | Add all changes to the index |
93-
| `add <file>` | Add a specific file to the index |
94-
| `add interactive` | Add changes interactively |
95-
| `add patch` | Add changes interactively (patch mode) |
96-
| `help` | Show main help message |
97-
| `help <command>` | Show help for a specific command |
98-
| `reset` | Hard reset to origin/<branch> and clean working directory |
99-
| `reset hard <commit>` | Hard reset to specified commit |
100-
| `reset soft <commit>` | Soft reset: move HEAD but keep changes staged |
101-
| `branch checkout` | Switch to an existing branch |
102-
| `branch checkout remote` | Create and checkout a local branch from the remote |
103-
| `branch contains <commit>` | Show branches containing a commit |
104-
| `branch create` | Create and checkout a new branch |
105-
| `branch current` | Show current branch name |
106-
| `branch delete` | Delete local branch |
107-
| `branch delete merged` | Delete local merged branch |
108-
| `branch info <branch>` | Show detailed branch information |
109-
| `branch list local` | List local branches |
110-
| `branch list remote` | List remote branches |
111-
| `branch list verbose` | Show detailed branch listing |
112-
| `branch move <branch> <commit>` | Move branch to specified commit |
113-
| `branch rename <old> <new>` | Rename a branch |
114-
| `branch set upstream <branch> <upstream>` | Set upstream for a branch |
115-
| `branch sort [date|name]` | List branches sorted by date or name |
116-
| `commit <message>` | Create commit with a message |
117-
| `commit allow empty` | Create an empty commit |
118-
| `commit amend` | Amend previous commit (editor) |
119-
| `commit amend no-edit` | Amend without editing commit message |
120-
| `commit fixup <commit>` | Create a fixup commit targeting <commit> |
121-
| `log graph` | Show log with graph |
122-
| `log simple` | Show simple historical log |
123-
| `fetch` | Fetch from the remote |
124-
| `fetch prune` | Fetch and clean stale references |
125-
| `pull current` | Pull current branch from remote repository |
126-
| `pull rebase` | Pull and rebase |
127-
| `push current` | Push current branch to remote repository |
128-
| `push force` | Force push current branch |
129-
| `remote add <name> <url>` | Add remote repository |
130-
| `remote list` | List all remote repositories |
131-
| `remote remove <name>` | Remove remote repository |
132-
| `remote set-url <name> <url>` | Change remote URL |
133-
| `status` | Show working tree status |
134-
| `status short` | Show concise status (porcelain format) |
135-
| `clean dirs` | Clean untracked directories |
136-
| `clean files` | Clean untracked files |
137-
| `clean interactive` | Clean files interactively |
138-
| `restore .` | Restore all files in working directory from index |
139-
| `restore <commit> <file>` | Restore file from specific commit |
140-
| `restore <file>` | Restore file in working directory from index |
141-
| `restore staged .` | Unstage all files |
142-
| `restore staged <file>` | Unstage file (restore from HEAD to index) |
143-
| `diff` | Show changes (git diff HEAD) |
144-
| `diff head` | Alias for default diff against HEAD |
145-
| `diff staged` | Show staged changes |
146-
| `diff unstaged` | Show unstaged changes |
147-
| `tag annotated <tag> <message>` | Create annotated tag |
148-
| `tag create <tag>` | Create tag |
149-
| `tag delete <tag>` | Delete tag |
150-
| `tag list` | List all tags |
151-
| `tag push` | Push tags to remote |
152-
| `tag show <tag>` | Show tag information |
153-
| `config get <key>` | Get a specific config value |
154-
| `config list` | List all configuration |
155-
| `config set <key> <value>` | Set a configuration value |
156-
| `hook disable <hook>` | Disable a hook |
157-
| `hook edit <hook>` | Edit a hook's contents |
158-
| `hook enable <hook>` | Enable a hook |
159-
| `hook install <hook>` | Install a hook |
160-
| `hook list` | List all hooks |
161-
| `hook uninstall <hook>` | Uninstall an existing hook |
162-
| `rebase <upstream>` | Rebase current branch onto <upstream> |
163-
| `rebase abort` | Abort an in-progress rebase |
164-
| `rebase autosquash` | Interactive rebase with --autosquash |
165-
| `rebase continue` | Continue an in-progress rebase |
166-
| `rebase interactive` | Interactive rebase |
167-
| `rebase skip` | Skip current patch and continue |
168-
| `stash` | Stash current changes |
169-
| `stash apply` | Apply stash without removing it |
170-
| `stash apply <stash>` | Apply specific stash without removing it |
171-
| `stash branch <branch>` | Create branch from stash |
172-
| `stash branch <branch> <stash>` | Create branch from specific stash |
173-
| `stash clear` | Remove all stashes |
174-
| `stash create` | Create stash and return object name |
175-
| `stash drop` | Remove the latest stash |
176-
| `stash drop <stash>` | Remove specific stash |
177-
| `stash list` | List all stashes |
178-
| `stash pop` | Apply and remove the latest stash |
179-
| `stash pop <stash>` | Apply and remove specific stash |
180-
| `stash push` | Save changes to new stash |
181-
| `stash push -m <message>` | Save changes to new stash with message |
182-
| `stash save <message>` | Save changes to new stash with message |
183-
| `stash show` | Show changes in stash |
184-
| `stash show <stash>` | Show changes in specific stash |
185-
| `stash store <object>` | Store stash object |
186-
| `debug-keys` | Show current keybindings |
187-
| `debug-keys raw` | Capture key sequences interactively |
188-
| `debug-keys raw <file>` | Capture key sequences and save them to a file |
189-
| `doctor` | Diagnose the local ggc installation |
190-
| `quit` | Exit interactive mode |
191-
| `version` | Display current ggc version |
19261
## References
19362

19463
- [ggc documentation site](https://bmf-san.github.io/ggc/) - Full user guide, install notes, configuration reference, and troubleshooting

tools/cmd/gendocs/main.go

Lines changed: 4 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
package main
33

44
import (
5-
"bufio"
65
"fmt"
76
"os"
87
"sort"
@@ -11,171 +10,14 @@ import (
1110
"github.com/bmf-san/ggc/v8/cmd/command"
1211
)
1312

14-
func main() {
15-
lines, err := readREADME()
16-
if err != nil {
17-
fmt.Fprintf(os.Stderr, "Error reading README: %v\n", err)
18-
os.Exit(1)
19-
}
20-
21-
startIdx, endIdx, err := findCommandSection(lines)
22-
if err != nil {
23-
fmt.Fprintf(os.Stderr, "Error finding command section: %v\n", err)
24-
os.Exit(1)
25-
}
26-
27-
tableStartIdx, tableEndIdx, err := findCommandTable(lines, startIdx, endIdx)
28-
if err != nil {
29-
fmt.Fprintf(os.Stderr, "Error finding command table: %v\n", err)
30-
os.Exit(1)
31-
}
13+
const commandsReferencePath = "docs/guide/commands.md"
3214

33-
newTable := generateCommandTable()
34-
35-
if err := writeUpdatedREADME(lines, tableStartIdx, tableEndIdx, newTable); err != nil {
36-
fmt.Fprintf(os.Stderr, "Error writing README: %v\n", err)
37-
os.Exit(1)
38-
}
39-
40-
fmt.Println("README.md command table updated successfully")
41-
42-
if err := writeCommandsReference("docs/guide/commands.md"); err != nil {
15+
func main() {
16+
if err := writeCommandsReference(commandsReferencePath); err != nil {
4317
fmt.Fprintf(os.Stderr, "Error writing commands reference: %v\n", err)
4418
os.Exit(1)
4519
}
46-
fmt.Println("docs/guide/commands.md regenerated from registry")
47-
}
48-
49-
func readREADME() ([]string, error) {
50-
file, err := os.Open("README.md")
51-
if err != nil {
52-
return nil, err
53-
}
54-
defer func() {
55-
_ = file.Close()
56-
}()
57-
58-
var lines []string
59-
scanner := bufio.NewScanner(file)
60-
for scanner.Scan() {
61-
lines = append(lines, scanner.Text())
62-
}
63-
64-
return lines, scanner.Err()
65-
}
66-
67-
func findCommandSection(lines []string) (int, int, error) {
68-
startIdx := -1
69-
endIdx := -1
70-
71-
for i, line := range lines {
72-
if strings.Contains(line, "### Available Commands") {
73-
startIdx = i
74-
}
75-
if startIdx != -1 && startIdx < i && strings.HasPrefix(line, "### ") && !strings.Contains(line, "Available Commands") {
76-
endIdx = i
77-
break
78-
}
79-
}
80-
81-
if startIdx == -1 {
82-
return 0, 0, fmt.Errorf("could not find '### Available Commands' section")
83-
}
84-
85-
return startIdx, endIdx, nil
86-
}
87-
88-
func findCommandTable(lines []string, startIdx, endIdx int) (int, int, error) {
89-
tableStartIdx := findTableStart(lines, startIdx, endIdx)
90-
if tableStartIdx == -1 {
91-
return 0, 0, fmt.Errorf("could not find command table")
92-
}
93-
94-
tableEndIdx := findTableEnd(lines, tableStartIdx, endIdx)
95-
return tableStartIdx, tableEndIdx, nil
96-
}
97-
98-
func findTableStart(lines []string, startIdx, endIdx int) int {
99-
for i := startIdx; i < len(lines) && (endIdx == -1 || i < endIdx); i++ {
100-
if strings.HasPrefix(lines[i], "| Command | Description |") {
101-
return i
102-
}
103-
}
104-
return -1
105-
}
106-
107-
func findTableEnd(lines []string, tableStartIdx, endIdx int) int {
108-
for i := tableStartIdx + 2; i < len(lines); i++ { // Skip header and separator
109-
if (endIdx != -1 && i >= endIdx) ||
110-
(!strings.HasPrefix(lines[i], "|") && strings.TrimSpace(lines[i]) != "") {
111-
return i
112-
}
113-
}
114-
return len(lines)
115-
}
116-
117-
func writeUpdatedREADME(lines []string, tableStartIdx, tableEndIdx int, newTable []string) error {
118-
var newLines []string
119-
newLines = append(newLines, lines[:tableStartIdx]...)
120-
newLines = append(newLines, newTable...)
121-
newLines = append(newLines, lines[tableEndIdx:]...)
122-
123-
output, err := os.Create("README.md")
124-
if err != nil {
125-
return err
126-
}
127-
defer func() {
128-
_ = output.Close()
129-
}()
130-
131-
for _, line := range newLines {
132-
if _, err := fmt.Fprintln(output, line); err != nil {
133-
return err
134-
}
135-
}
136-
137-
return nil
138-
}
139-
140-
func generateCommandTable() []string {
141-
registry := command.NewRegistry()
142-
commands := registry.VisibleCommands()
143-
144-
// Sort commands by category, then by name
145-
sort.Slice(commands, func(i, j int) bool {
146-
if commands[i].Category != commands[j].Category {
147-
return command.CategoryOrder(commands[i].Category) < command.CategoryOrder(commands[j].Category)
148-
}
149-
return commands[i].Name < commands[j].Name
150-
})
151-
152-
var table []string
153-
table = append(table, "| Command | Description |")
154-
table = append(table, "|--------|-------------|")
155-
156-
for i := range commands {
157-
cmd := &commands[i]
158-
if len(cmd.Subcommands) == 0 {
159-
table = append(table, fmt.Sprintf("| `%s` | %s |", cmd.Name, cmd.Summary))
160-
} else {
161-
// Sort subcommands by name
162-
subcommands := make([]command.SubcommandInfo, 0, len(cmd.Subcommands))
163-
for _, sub := range cmd.Subcommands {
164-
if !sub.Hidden {
165-
subcommands = append(subcommands, sub)
166-
}
167-
}
168-
sort.Slice(subcommands, func(i, j int) bool {
169-
return subcommands[i].Name < subcommands[j].Name
170-
})
171-
172-
for _, sub := range subcommands {
173-
table = append(table, fmt.Sprintf("| `%s` | %s |", sub.Name, sub.Summary))
174-
}
175-
}
176-
}
177-
178-
return table
20+
fmt.Printf("%s regenerated from registry\n", commandsReferencePath)
17921
}
18022

18123
func writeCommandsReference(path string) error {

0 commit comments

Comments
 (0)