mirror of
https://github.com/harivansh-afk/forge.nvim.git
synced 2026-04-15 03:00:46 +00:00
doc: minimize readme
This commit is contained in:
parent
2658c47af1
commit
354c5000c0
1 changed files with 50 additions and 316 deletions
366
README.md
366
README.md
|
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
**Forge-agnostic git workflow for Neovim**
|
**Forge-agnostic git workflow for Neovim**
|
||||||
|
|
||||||
PR, issue, and CI workflows across GitHub, GitLab, and more — without leaving
|
PR, issue, and CI workflows across GitHub, GitLab, and Codeberg/Gitea/Forgejo —
|
||||||
your editor.
|
without leaving your editor.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Automatic forge detection from git remote (GitHub via `gh`, GitLab via `glab`,
|
- Automatic forge detection from git remote (`gh`, `glab`, `tea`)
|
||||||
Codeberg/Gitea/Forgejo via `tea`)
|
|
||||||
- PR lifecycle: list, create (compose buffer with template discovery, diff stat,
|
- PR lifecycle: list, create (compose buffer with template discovery, diff stat,
|
||||||
reviewers), checkout, worktree, review diff, merge, approve, close/reopen,
|
reviewers), checkout, worktree, review diff, merge, approve, close/reopen,
|
||||||
draft toggle
|
draft toggle
|
||||||
|
|
@ -17,27 +16,52 @@ your editor.
|
||||||
- Code review via [diffs.nvim](https://github.com/barrettruth/diffs.nvim) with
|
- Code review via [diffs.nvim](https://github.com/barrettruth/diffs.nvim) with
|
||||||
unified/split toggle and quickfix navigation
|
unified/split toggle and quickfix navigation
|
||||||
- Commit and branch browsing with checkout, diff, and URL generation
|
- Commit and branch browsing with checkout, diff, and URL generation
|
||||||
- File/line permalink generation and yanking (commit and branch URLs)
|
- File/line permalink generation and yanking
|
||||||
- [fzf-lua](https://github.com/ibhagwan/fzf-lua) pickers with contextual
|
- [fzf-lua](https://github.com/ibhagwan/fzf-lua) pickers with contextual
|
||||||
keybinds
|
keybinds
|
||||||
- Pluggable source registration for custom or self-hosted forges
|
- Pluggable source registration for custom or self-hosted forges
|
||||||
|
|
||||||
## Dependencies
|
## Requirements
|
||||||
|
|
||||||
- Neovim 0.10.0+
|
- Neovim 0.10.0+
|
||||||
- [fzf-lua](https://github.com/ibhagwan/fzf-lua)
|
- [fzf-lua](https://github.com/ibhagwan/fzf-lua)
|
||||||
- At least one forge CLI:
|
- At least one forge CLI: [`gh`](https://cli.github.com/),
|
||||||
- [`gh`](https://cli.github.com/) for GitHub
|
[`glab`](https://gitlab.com/gitlab-org/cli), or
|
||||||
- [`glab`](https://gitlab.com/gitlab-org/cli) for GitLab
|
[`tea`](https://gitea.com/gitea/tea)
|
||||||
- [`tea`](https://gitea.com/gitea/tea) for Codeberg/Gitea/Forgejo
|
- (Optional) [diffs.nvim](https://github.com/barrettruth/diffs.nvim) for review
|
||||||
- [vim-fugitive](https://github.com/tpope/vim-fugitive) (optional, for fugitive
|
mode
|
||||||
keymaps and split diff)
|
- (Optional) [vim-fugitive](https://github.com/tpope/vim-fugitive) for split
|
||||||
- [diffs.nvim](https://github.com/barrettruth/diffs.nvim) (optional, for review
|
diff and fugitive keymaps
|
||||||
mode)
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### [lazy.nvim](https://github.com/folke/lazy.nvim)
|
Install with your package manager of choice or via
|
||||||
|
[luarocks](https://luarocks.org/modules/barrettruth/forge.nvim):
|
||||||
|
|
||||||
|
```
|
||||||
|
luarocks install forge.nvim
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
```vim
|
||||||
|
:help forge.nvim
|
||||||
|
```
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**Q: How do I configure forge.nvim?**
|
||||||
|
|
||||||
|
Configure via `vim.g.forge` before the plugin loads. All fields are optional:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
vim.g.forge = {
|
||||||
|
sources = { gitlab = { hosts = { 'gitlab.mycompany.com' } } },
|
||||||
|
display = { icons = { open = '', merged = '', closed = '' } },
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q: How do I install with lazy.nvim?**
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
{
|
{
|
||||||
|
|
@ -46,309 +70,19 @@ your editor.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### [mini.deps](https://github.com/echasnovski/mini.deps)
|
**Q: How do I create a PR?**
|
||||||
|
|
||||||
```lua
|
`<c-g>` to open the picker, select Pull Requests, then `ctrl-a` to compose. Or
|
||||||
MiniDeps.add({
|
from a fugitive buffer: `cpr` (compose), `cpd` (draft), `cpf` (instant from
|
||||||
source = 'barrettruth/forge.nvim',
|
commits), `cpw` (push and open web).
|
||||||
depends = { 'ibhagwan/fzf-lua' },
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### [luarocks](https://luarocks.org/modules/barrettruth/forge.nvim)
|
**Q: Does review mode require diffs.nvim?**
|
||||||
|
|
||||||
```
|
Yes. Without [diffs.nvim](https://github.com/barrettruth/diffs.nvim), diff
|
||||||
luarocks install forge.nvim
|
actions and review toggling are unavailable.
|
||||||
```
|
|
||||||
|
|
||||||
### Manual
|
**Q: How does forge detection work?**
|
||||||
|
|
||||||
```sh
|
forge.nvim reads the `origin` remote URL and matches against known hosts and any
|
||||||
git clone https://github.com/barrettruth/forge.nvim \
|
custom `sources.<name>.hosts` entries. The first match wins, and the CLI must be
|
||||||
~/.local/share/nvim/site/pack/plugins/start/forge.nvim
|
in `$PATH`.
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
forge.nvim works through two entry points: the `:Forge` command and the `<c-g>`
|
|
||||||
picker.
|
|
||||||
|
|
||||||
`:Forge` with no arguments (or `<c-g>`) opens the top-level picker — PRs,
|
|
||||||
issues, CI, commits, branches, worktrees, and browse actions. Each sub-picker
|
|
||||||
has contextual keybinds shown in the fzf header.
|
|
||||||
|
|
||||||
PR creation opens a compose buffer (markdown) pre-filled from commit messages
|
|
||||||
and repo templates. First line is the title, everything after the blank line is
|
|
||||||
the body. Draft, reviewers, and base branch are set in the HTML comment block
|
|
||||||
below. Write (`:w`) to push and create.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Configure via `vim.g.forge`. All fields are optional — defaults shown below.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
vim.g.forge = {
|
|
||||||
ci = { lines = 10000 },
|
|
||||||
sources = {},
|
|
||||||
keys = {
|
|
||||||
picker = '<c-g>',
|
|
||||||
next_qf = ']q', prev_qf = '[q',
|
|
||||||
next_loc = ']l', prev_loc = '[l',
|
|
||||||
review_toggle = 's',
|
|
||||||
terminal_open = 'gx',
|
|
||||||
fugitive = {
|
|
||||||
create = 'cpr', create_draft = 'cpd',
|
|
||||||
create_fill = 'cpf', create_web = 'cpw',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
picker_keys = {
|
|
||||||
pr = {
|
|
||||||
checkout = 'default', diff = 'ctrl-d', worktree = 'ctrl-w',
|
|
||||||
checks = 'ctrl-t', browse = 'ctrl-x', manage = 'ctrl-e',
|
|
||||||
create = 'ctrl-a', toggle = 'ctrl-o', refresh = 'ctrl-r',
|
|
||||||
},
|
|
||||||
issue = { browse = 'default', close_reopen = 'ctrl-s', toggle = 'ctrl-o', refresh = 'ctrl-r' },
|
|
||||||
checks = { log = 'default', browse = 'ctrl-x', failed = 'ctrl-f', passed = 'ctrl-p', running = 'ctrl-n', all = 'ctrl-a' },
|
|
||||||
ci = { log = 'default', browse = 'ctrl-x', refresh = 'ctrl-r' },
|
|
||||||
commits = { checkout = 'default', diff = 'ctrl-d', browse = 'ctrl-x', yank = 'ctrl-y' },
|
|
||||||
branches = { diff = 'ctrl-d', browse = 'ctrl-x' },
|
|
||||||
},
|
|
||||||
display = {
|
|
||||||
icons = { open = '+', merged = 'm', closed = 'x', pass = '*', fail = 'x', pending = '~', skip = '-', unknown = '?' },
|
|
||||||
widths = { title = 45, author = 15, name = 35, branch = 25 },
|
|
||||||
limits = { pulls = 100, issues = 100, runs = 30 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Set `keys = false` to disable all keymaps. Set `picker_keys = false` to disable
|
|
||||||
all picker keybinds. Set any individual key to `false` to disable it.
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
Disable quickfix/loclist keymaps:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
vim.g.forge = {
|
|
||||||
keys = { next_qf = false, prev_qf = false, next_loc = false, prev_loc = false },
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Nerd font icons:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
vim.g.forge = {
|
|
||||||
display = {
|
|
||||||
icons = { open = '', merged = '', closed = '', pass = '', fail = '', pending = '', skip = '', unknown = '' },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Self-hosted GitLab:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
vim.g.forge = {
|
|
||||||
sources = { gitlab = { hosts = { 'gitlab.mycompany.com' } } },
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Override PR picker bindings:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
vim.g.forge = {
|
|
||||||
picker_keys = { pr = { checkout = 'ctrl-o', diff = 'default' } },
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
`:Forge` with no arguments opens the top-level picker. Subcommands:
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
| --------------------------------------------- | --------------------------------- |
|
|
||||||
| `:Forge pr` | List open PRs |
|
|
||||||
| `:Forge pr --state={open,closed,all}` | List PRs by state |
|
|
||||||
| `:Forge pr create [--draft] [--fill] [--web]` | Create PR |
|
|
||||||
| `:Forge pr checkout {num}` | Checkout PR branch |
|
|
||||||
| `:Forge pr diff {num}` | Review PR diff |
|
|
||||||
| `:Forge pr worktree {num}` | Fetch PR into worktree |
|
|
||||||
| `:Forge pr checks {num}` | Show PR checks |
|
|
||||||
| `:Forge pr browse {num}` | Open PR in browser |
|
|
||||||
| `:Forge pr manage {num}` | Merge/approve/close/draft actions |
|
|
||||||
| `:Forge issue` | List all issues |
|
|
||||||
| `:Forge issue --state={open,closed,all}` | List issues by state |
|
|
||||||
| `:Forge issue browse {num}` | Open issue in browser |
|
|
||||||
| `:Forge issue close {num}` | Close issue |
|
|
||||||
| `:Forge issue reopen {num}` | Reopen issue |
|
|
||||||
| `:Forge ci` | CI runs for current branch |
|
|
||||||
| `:Forge ci --all` | CI runs for all branches |
|
|
||||||
| `:Forge commit` | Browse commits |
|
|
||||||
| `:Forge commit checkout {sha}` | Checkout commit |
|
|
||||||
| `:Forge commit diff {sha}` | Review commit diff |
|
|
||||||
| `:Forge commit browse {sha}` | Open commit in browser |
|
|
||||||
| `:Forge branch` | Browse branches |
|
|
||||||
| `:Forge branch diff {name}` | Review branch diff |
|
|
||||||
| `:Forge branch browse {name}` | Open branch in browser |
|
|
||||||
| `:Forge worktree` | List worktrees |
|
|
||||||
| `:Forge browse [--root] [--commit]` | Open file/repo/commit in browser |
|
|
||||||
| `:Forge yank [--commit]` | Yank permalink for file/line |
|
|
||||||
| `:Forge review end` | End review session |
|
|
||||||
| `:Forge review toggle` | Toggle split/unified review |
|
|
||||||
| `:Forge cache clear` | Clear all caches |
|
|
||||||
|
|
||||||
## Keymaps
|
|
||||||
|
|
||||||
### Global
|
|
||||||
|
|
||||||
| Key | Mode | Description |
|
|
||||||
| ----------- | ---- | -------------------------------- |
|
|
||||||
| `<c-g>` | n, v | Open forge picker |
|
|
||||||
| `]q` / `[q` | n | Next/prev quickfix entry (wraps) |
|
|
||||||
| `]l` / `[l` | n | Next/prev loclist entry (wraps) |
|
|
||||||
|
|
||||||
### Fugitive buffer
|
|
||||||
|
|
||||||
Active in `fugitive` filetype buffers when a forge is detected.
|
|
||||||
|
|
||||||
| Key | Description |
|
|
||||||
| ----- | ----------------------------------- |
|
|
||||||
| `cpr` | Create PR (compose buffer) |
|
|
||||||
| `cpd` | Create draft PR |
|
|
||||||
| `cpf` | Create PR from commits (no compose) |
|
|
||||||
| `cpw` | Push and open web creation |
|
|
||||||
|
|
||||||
### Review
|
|
||||||
|
|
||||||
Active during a review session.
|
|
||||||
|
|
||||||
| Key | Description |
|
|
||||||
| --- | ------------------------- |
|
|
||||||
| `s` | Toggle unified/split diff |
|
|
||||||
|
|
||||||
### Terminal (log buffers)
|
|
||||||
|
|
||||||
Active on CI/check log terminals when a URL is available.
|
|
||||||
|
|
||||||
| Key | Description |
|
|
||||||
| ---- | ------------------------- |
|
|
||||||
| `gx` | Open run/check in browser |
|
|
||||||
|
|
||||||
## Picker Actions
|
|
||||||
|
|
||||||
Keybinds shown in the fzf header. `default` = `enter`.
|
|
||||||
|
|
||||||
| Picker | Key | Action |
|
|
||||||
| ------------ | ------------------------------ | ---------------------------------- |
|
|
||||||
| **PR** | `enter` | Checkout |
|
|
||||||
| | `ctrl-d` | Review diff |
|
|
||||||
| | `ctrl-w` | Worktree |
|
|
||||||
| | `ctrl-t` | Checks |
|
|
||||||
| | `ctrl-x` | Browse |
|
|
||||||
| | `ctrl-e` | Manage (merge/approve/close/draft) |
|
|
||||||
| | `ctrl-a` | Create new |
|
|
||||||
| | `ctrl-o` | Cycle state (open/closed/all) |
|
|
||||||
| | `ctrl-r` | Refresh |
|
|
||||||
| **Issue** | `enter` | Browse |
|
|
||||||
| | `ctrl-s` | Close/reopen |
|
|
||||||
| | `ctrl-o` | Cycle state |
|
|
||||||
| | `ctrl-r` | Refresh |
|
|
||||||
| **Checks** | `enter` | View log (tails if running) |
|
|
||||||
| | `ctrl-x` | Browse |
|
|
||||||
| | `ctrl-f` / `ctrl-p` / `ctrl-n` | Filter: failed / passed / running |
|
|
||||||
| | `ctrl-a` | Show all |
|
|
||||||
| **CI** | `enter` | View log (tails if running) |
|
|
||||||
| | `ctrl-x` | Browse |
|
|
||||||
| | `ctrl-r` | Refresh |
|
|
||||||
| **Commits** | `enter` | Checkout (detached) |
|
|
||||||
| | `ctrl-d` | Review diff |
|
|
||||||
| | `ctrl-x` | Browse |
|
|
||||||
| | `ctrl-y` | Yank hash |
|
|
||||||
| **Branches** | `ctrl-d` | Review diff |
|
|
||||||
| | `ctrl-x` | Browse |
|
|
||||||
|
|
||||||
## Custom Sources
|
|
||||||
|
|
||||||
Register a custom forge source for self-hosted or alternative platforms:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
require('forge').register('mygitea', require('my_gitea_source'))
|
|
||||||
```
|
|
||||||
|
|
||||||
Route remotes to your source by host:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
vim.g.forge = {
|
|
||||||
sources = { mygitea = { hosts = { 'gitea.internal.dev' } } },
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A source is a table implementing the `forge.Forge` interface. Required fields:
|
|
||||||
`name` (string), `cli` (string, checked via `executable()`), `kinds`
|
|
||||||
(`{ issue, pr }`), and `labels` (`{ issue, pr, pr_one, pr_full, ci }`).
|
|
||||||
|
|
||||||
Required methods (all receive `self`): `list_pr_json_cmd`,
|
|
||||||
`list_issue_json_cmd`, `pr_json_fields`, `issue_json_fields`, `view_web`,
|
|
||||||
`browse`, `browse_root`, `browse_branch`, `browse_commit`, `checkout_cmd`,
|
|
||||||
`yank_branch`, `yank_commit`, `fetch_pr`, `pr_base_cmd`, `pr_for_branch_cmd`,
|
|
||||||
`checks_cmd`, `check_log_cmd`, `check_tail_cmd`, `list_runs_json_cmd`,
|
|
||||||
`list_runs_cmd`, `normalize_run`, `run_log_cmd`, `run_tail_cmd`, `merge_cmd`,
|
|
||||||
`approve_cmd`, `repo_info`, `pr_state`, `close_cmd`, `reopen_cmd`,
|
|
||||||
`close_issue_cmd`, `reopen_issue_cmd`, `draft_toggle_cmd`, `create_pr_cmd`,
|
|
||||||
`create_pr_web_cmd`, `default_branch_cmd`, `template_paths`.
|
|
||||||
|
|
||||||
See `lua/forge/github.lua`, `lua/forge/gitlab.lua`, or `lua/forge/codeberg.lua`
|
|
||||||
for complete implementations. The `forge.Forge` class definition with full type
|
|
||||||
annotations is in `lua/forge/init.lua`.
|
|
||||||
|
|
||||||
### Skeleton
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local M = {
|
|
||||||
name = 'mygitea',
|
|
||||||
cli = 'tea',
|
|
||||||
kinds = { issue = 'issues', pr = 'pulls' },
|
|
||||||
labels = { issue = 'Issues', pr = 'PRs', pr_one = 'PR', pr_full = 'Pull Requests', ci = 'CI/CD' },
|
|
||||||
}
|
|
||||||
|
|
||||||
function M:list_pr_json_cmd(state)
|
|
||||||
return { 'tea', 'pr', 'list', '--state', state, '--output', 'json' }
|
|
||||||
end
|
|
||||||
|
|
||||||
function M:pr_json_fields()
|
|
||||||
return { number = 'number', title = 'title', branch = 'head', state = 'state', author = 'poster', created_at = 'created_at' }
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
```
|
|
||||||
|
|
||||||
## Health
|
|
||||||
|
|
||||||
Run `:checkhealth forge` to verify your setup. Checks for `git`, forge CLIs
|
|
||||||
(`gh`, `glab`, `tea`), required plugins (`fzf-lua`), optional plugins
|
|
||||||
(`diffs.nvim`, `vim-fugitive`), and any registered custom sources.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
**Q: How do I create a PR?** `<c-g>` -> Pull Requests -> `ctrl-a` to compose. Or
|
|
||||||
from fugitive: `cpr` (compose), `cpd` (draft), `cpf` (instant), `cpw` (web).
|
|
||||||
|
|
||||||
**Q: Does review mode require diffs.nvim?** Yes. Without
|
|
||||||
[diffs.nvim](https://github.com/barrettruth/diffs.nvim), the diff action and
|
|
||||||
review toggling are unavailable.
|
|
||||||
|
|
||||||
**Q: How does forge detection work?** forge.nvim reads the `origin` remote URL
|
|
||||||
and matches against known hosts and any custom `sources.<name>.hosts`. The first
|
|
||||||
match wins, and the CLI must be in `$PATH`.
|
|
||||||
|
|
||||||
**Q: Can I use this with self-hosted GitLab/Gitea?** Yes. Add your host to
|
|
||||||
`vim.g.forge.sources`. See the [examples](#examples).
|
|
||||||
|
|
||||||
**Q: What does `ctrl-o` do in pickers?** Cycles the state filter: open -> closed
|
|
||||||
-> all -> open.
|
|
||||||
|
|
||||||
**Q: How do I merge/approve/close a PR?** `ctrl-e` on a PR in the picker opens
|
|
||||||
the manage picker. Available actions depend on your repository permissions.
|
|
||||||
|
|
||||||
**Q: Does this work without a forge remote?** Partially. Commits, branches, and
|
|
||||||
worktrees work in any git repo. PRs, issues, CI, and browse require a detected
|
|
||||||
forge.
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue