Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Make the CLI speak git's dialect

f956874883e9fdbb95f13b5429bb1104dcec2756 · Keenan Nemetz · 17d ago · parent c1e3e3ca

Make the CLI speak git's dialect

The flags were wrong in the way that matters: `-title` is a single-dash long
option, which anything that thinks like getopt reads as `-t -i -t -l -e`. Git's
conventions (gitcli(7)) are `--long`, `-o`, and no bundling, so this replaces the
parser and every call site.

Now:

- `--long value`, `--long=value`, `-o value`, `-ovalue`.
- Short options are never bundled: `-ab` is not `-a -b`. For an option that takes
  a value, `-mm` is the stuck form and means the value "m"; for a boolean, a
  bundle is an error.
- `-o=value` is refused with a message pointing at `-ov` or `--long=v`, because
  silently taking "=value" is the kind of thing that costs an hour later.
- Options come before arguments, and an option after an argument is an error
  naming the argument rather than something to guess about.
- `--` and `--end-of-options` end option parsing; `-h`/`--help` print the
  subcommand's usage and exit 0; usage mistakes exit 129, as git does.
- `-m`/`-F` mirror `git commit`: mutually exclusive, `-F -` reads stdin.
- `-l` is repeatable; comma-separated still works for convenience.
- `issue list --format` takes `%(id)`, `%(shortid)`, `%(state)`, `%(author)`,
  `%(title)`, `%(labels)`, `%(created)` for scripts.
- `vole.author` is consulted before `user.name` for the default author.

Two bugs came out of the change, both caught by tests rather than by reading:

- The new parser stored `--config` as an ordinary value and never copied it to
  the path the commands actually use, so every command silently fell back to
  ./vole.toml. A test that rebuilds an index in a temp directory failed loudly.
- The integration test itself passed `-config`, which the new parser read as
  `-c onfig` — exactly the ambiguity this commit removes, in the wild.

Verified by hand against a live instance: repeatable `-l`, stuck `--title=x`,
`--format`, `issue comment -m text <id>` accepted, `issue comment <id> -m text`
refused with exit 129 and "options must come before arguments", `-h` exiting 0,
and a missing option value exiting 129.

docs/roadmap.md +1

@@ -321,6 +321,7 @@ Filled in as phases land. Append; don't rewrite.
321 321 | 2026-10-22 | `receive.maxInputSize` replaces the HTTP byte cap | git enforces it on both transports; the proxy never buffers a pack |
322 322 | 2026-10-22 | Issues are push-based on `refs/heads/issues`, IDs are client-generated ULIDs | No authenticated web write surface exists, and a branch makes issues travel with a clone |
323 323 | 2026-10-22 | Issue trees validated in `pre-receive` | Reject bad data at the boundary instead of rendering it |
324 | 2026-10-22 | CLI follows gitcli(7): `--long`, `-o`, options first, `--`, exit 129 | A tool that speaks git's dialect composes with git and with muscle memory |
324 325
325 326 ## Open questions
326 327