## The bug
`tea comment <idx> "body"` hangs forever in any non-TTY context — CI pipelines, subshells, agent harnesses, scripts — unless the user explicitly redirects stdin with `< /dev/null`.
### Reproduction (released `tea` 0.14.1)
```
# Plain positional body — hangs forever
$ tea comment --repo owner/repo 1 "body text"
# Heredoc body — hangs forever
$ tea comment --repo owner/repo 1 "$(cat <<'EOM'
body text
EOM
)"
# Workaround that works (but shouldn't be necessary)
$ tea comment --repo owner/repo 1 "body text" < /dev/null
```
Verified on `gitea.com` (server 1.26.0+dev) with `tea 0.14.1`. Both hanging cases hit a 30s timeout in testing; in normal shell use they hang indefinitely.
## Root cause
`cmd/comment.go:58-65`:
```go
body := strings.Join(ctx.Args().Tail(), " ")
if interact.IsStdinPiped() {
if bodyStdin, err := io.ReadAll(ctx.Reader); err != nil {
return err
} else if len(bodyStdin) != 0 {
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
}
}
```
`interact.IsStdinPiped()` is implemented as `!term.IsTerminal(os.Stdin)` — true for *any* non-TTY stdin, not just for piped data. When tea enters this branch in a subshell where stdin is open but no producer ever writes to it (the typical case for shell scripts and automation), `io.ReadAll` blocks waiting for an EOF that never arrives.
## Fix
Only consume stdin when the user did **not** supply a positional body. If they passed a body via args, that's their content — ignore stdin entirely.
```go
if len(body) == 0 && interact.IsStdinPiped() {
// ... read stdin ...
body = string(bodyStdin)
}
```
## Behavior matrix
| Invocation | Before | After |
|---|---|---|
| `tea comment <idx> "body"` (TTY) | works | works |
| `tea comment <idx> "body"` (non-TTY, no redirect) | **hangs forever** | **works** |
| `tea comment <idx> "body" < /dev/null` | works | works |
| `echo body \| tea comment <idx>` | works | works |
| `tea comment <idx> "prefix"` + piped stdin | "prefix" + stdin concatenated | positional body wins, stdin ignored |
| `tea comment <idx>` (TTY, no body, no pipe) | opens editor | opens editor |
The one behavior change is the prefix-plus-stdin case (5th row). I couldn't find anyone relying on that pattern and it wasn't documented; defaulting to "positional body wins" matches the principle of least surprise.
## Verification
Confirmed each row against `dinsmoor/tea-testing` issue #1 on `gitea.com` with the patched binary. Previously-hanging invocations now post in <0.5s. Piped-stdin path unchanged.
---
This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step.
*pull request created by Tyler's lovingly wrangled demon machine <3*
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/1011
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Tyler <tyler@dinsmoor.us>
Co-committed-by: Tyler <tyler@dinsmoor.us>
Closes#1013.
## Background
While trying to use `tea` from a non-interactive context I hit a friction point: after `tea login add` succeeded, plain `git push` over HTTPS still prompted for credentials. I filed #1013 as a feature request to add credential helper integration — then found, on reading the source, that the integration **already exists**:
- `tea login add` accepts `--helper` (alias `-j`), which calls `task.SetupHelper` to register a credential helper in `~/.gitconfig`.
- `tea login helper get` correctly implements git's credential protocol, reading the request from stdin and returning `protocol`/`host`/`username`/`password` lines.
- `tea login helper setup` does the same for every configured login.
I verified end-to-end that this works as advertised: after `tea login helper setup`, an HTTPS `git push` against a configured Gitea host authenticates silently using the stored token, no prompts, with `GIT_TERMINAL_PROMPT=0` set as a safety check.
So the feature is fine. The problem is that nobody can find it:
| Surface | Before | Issue |
|---|---|---|
| Flag name on `tea login add` | `--helper` (alias `-j`) | Generic; nothing tying it to git or credentials |
| Flag usage text | `"Add helper"` | Says nothing |
| `tea login helper` command | `Hidden: true` | Not in `tea login --help` |
| `tea login helper` usage | `"Git helper"` | Says nothing |
| `tea login helper` description | `"Git helper"` | Same string again |
| `store/erase` subcommand description | `"Command drops"` | Sentence fragment, no meaning |
| `setup` subcommand description | `"Setup helper to tea authenticate"` | Awkward, doesn't explain what it touches |
| `get` subcommand description | `"Get token to auth"` | Doesn't mention git, stdin, or the credential protocol |
| Mention in `tea login add --help` | None | Feature is invisible |
## What this patch does
Purely cosmetic / documentation changes — **no behavior changes**:
1. Renames `--helper` to `--git-credentials`, keeping `--helper` and `-j` as aliases so existing scripts and muscle memory keep working.
2. Removes `Hidden: true` from `tea login helper` so it appears in `tea login --help`.
3. Rewrites every placeholder `Usage` and `Description` string in the helper command tree to describe what the thing actually does.
4. Expands the top-level `Description` of `tea login add` to mention the option and explain what it does.
5. Prints a one-line hint after a successful non-helper login: `Tip: pass --git-credentials (or run 'tea login helper setup') to authenticate 'git push' and 'git clone' over HTTPS with this token.`
The credential helper protocol implementation, `SetupHelper`'s gitconfig writes, and the `get`/`store`/`setup` action functions are all unchanged.
## Help output after the patch
```
$ tea login --help
COMMANDS:
...
helper, git-credential Act as a git credential helper for stored Gitea logins
...
$ tea login helper --help
NAME:
tea logins helper - Act as a git credential helper for stored Gitea logins
DESCRIPTION:
Speaks git's credential helper protocol so that HTTPS push and clone
operations against your configured Gitea instances authenticate silently
using the tokens tea already stores.
Typical use is automatic: 'tea login add --git-credentials' (or 'tea login
helper setup' for existing logins) registers '!tea login helper' as a
credential helper in ~/.gitconfig. Git then invokes the 'get' subcommand
when it needs credentials for a configured host.
COMMANDS:
store, erase No-op (git credential protocol store/erase)
setup Register tea as a git credential helper for every configured login
get Return the stored token for a URL (git credential protocol)
```
## Open questions for the reviewer
A few choices in here that are subjective — happy to change any of them:
- **Flag name**: `--git-credentials` was the first clear name I tried. `--credential-helper` and `--git-helper` are also reasonable. Or keep `--helper` as canonical and just fix its usage text.
- **Canonical subcommand name**: I kept `helper` as canonical with `git-credential` as alias, matching what was already there. Could flip this — `gh` uses `gh auth git-credential` as canonical with no `helper` form.
- **Should `--git-credentials` default to true?** Most users probably want it on; the current opt-in design surprises them. But flipping the default is a behavior change so I left it alone here.
- **Should the hint be silenced by an env var or a config flag?** I left it always-on for the people who need to see it; can gate it if it bothers automation users.
- **Teardown on `tea login delete`** would parallel the setup behavior, but is genuinely a separate change. Not in this PR.
---
This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step.
*pull request created by Tyler's lovingly wrangled demon machine <3*
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/1014
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Tyler <tyler@dinsmoor.us>
Co-committed-by: Tyler <tyler@dinsmoor.us>
## Summary
Add first-class `tea wiki` commands backed by the existing Gitea wiki API and SDK support.
## What this adds
- `tea wiki list`
- `tea wiki view <page>`
- `tea wiki revisions <page>`
- `tea wiki create`
- `tea wiki edit <page>`
- `tea wiki delete <page>`
## Implementation details
- registers a new top-level `wiki` entity command
- keeps command logic under `cmd/wiki/`
- adds wiki-specific renderers in `modules/print/wiki.go`
- adds wiki task helpers in `modules/task/wiki.go`
- reuses existing repo/login/output/pagination patterns used elsewhere in `tea`
- base64-encodes wiki content for create/edit API calls
- requires explicit `--confirm` for delete
- preserves the current page title during edit when `--title` is omitted
## Test coverage
The PR is intentionally split into two commits:
1. `feat: add wiki CLI commands`
2. `test: add wiki integration coverage`
Validation performed:
- focused command, task, and print tests for the new wiki functionality
- integration coverage for the wiki command lifecycle
- `make lint`
- `make fmt-check`
- `make docs-check`
- `make build`
- upstream PR CI passed:
- `check-and-test / Integration Test`
- `check-and-test / Lint Build And Unit Coverage`
## Motivation
This makes `tea` a better interface for both human and agent-driven workflows by exposing wiki operations as stable first-class CLI commands instead of requiring ad-hoc API calls or custom wrappers.
---
Generated by Hermes Agent with GPT-5.4
---------
Co-authored-by: nitro <nitro@nitroui-Macmini.local>
Reviewed-on: https://gitea.com/gitea/tea/pulls/998
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: kuil09 <202447+kuil09@noreply.gitea.com>
Co-committed-by: kuil09 <202447+kuil09@noreply.gitea.com>
## What this PR does
`tea issues list --assignee USERNAME` currently returns every issue regardless of the assignee value — even nonexistent users return a full unfiltered list. Discovered against **tea v0.14.0** (with go-sdk v0.24.1) and reproduced on current `master` (commit `dd81b33`). This PR fixes that.
## Root cause
Two distinct bugs on the same flag, both in `cmd/issues/list.go`:
1. **Per-repo path** (`tea issues list --repo OWNER/REPO --assignee USER`): the code reads `ctx.String("assigned-to")` for `AssignedBy`, but the flag is defined as `--assignee` in `cmd/flags/issue_pr.go:66`. The lookup always returns `""`, so the SDK omits the `assigned_by` query parameter and the API returns everything.
2. **Global path** (`tea issues list --assignee USER`, no `--repo`): this hits `/repos/issues/search`, which silently ignores `assigned_by`. Even after fix#1 the no-repo form would still return unfiltered results. Verified directly:
- `GET /repos/issues/search?assigned_by=USER&owner=ORG&state=open` → all open issues
- `GET /repos/issues/search?assigned=true&owner=ORG&state=open` → only the issues assigned to the authenticated user
The endpoint only supports `assigned=true` (boolean self-filter), not arbitrary-user filtering, and `ListIssueOption` doesn't expose that field. Rather than misleading the caller, the no-repo path now returns a clear error.
## Changes
Both changes are in `cmd/issues/list.go`:
1. Read `ctx.String("assignee")` instead of the non-existent flag name `"assigned-to"` (lines 80 and 97).
2. In the no-`--repo` branch, return `errors.New("--assignee requires --repo (...)")` when the flag is set.
`cmd/pulls/list.go` does not expose an assignee filter, so it's unaffected. The `--author` mapping (`CreatedBy ← ctx.String("author")`) was already correct and is the model the fix follows.
## Manual verification
Tested against a local Gitea instance with three open issues (only one assigned to the test user):
| Command | Before | After |
|---|---|---|
| `tea issues list --repo X --assignee me` | all 3 | only the 1 assigned ✓ |
| `tea issues list --repo X --assignee nonexistent` | all 3 | `Error: not found` ✓ |
| `tea issues list --repo X --author me` | only the 1 (control) | unchanged ✓ |
| `tea issues list --assignee me` (no `--repo`) | all 3 (silent) | clear error ✓ |
| `tea issues list` (no flags) | all 3 | unchanged ✓ |
---------
Co-authored-by: claude_1 <claude_1@bot.gqx.lol>
Reviewed-on: https://gitea.com/gitea/tea/pulls/971
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Oleksii Zaremskyi <grossqx@gmail.com>
Co-committed-by: Oleksii Zaremskyi <grossqx@gmail.com>
## Summary
\`Page: -1\` in the Gitea SDK calls \`setDefaults()\` which sets both \`Page=0\` and \`PageSize=0\`, resulting in \`?page=0&limit=0\` being sent to the server. The server interprets \`limit=0\` as "use server default" (typically 30 items via \`DEFAULT_PAGING_NUM\`), not "return everything". Any resource beyond the first page of results was silently invisible.
This affected 8 call sites, with the most user-visible impact being \`tea issues edit --add-labels\` and \`tea pulls edit --add-labels\` silently failing to apply labels on repositories with more than ~30 labels.
## Affected call sites
| File | API call | User-visible impact |
|---|---|---|
| \`modules/task/labels.go\` | \`ListRepoLabels\` | \`issues/pulls edit --add-labels\` fails silently |
| \`modules/interact/issue_create.go\` | \`ListRepoLabels\` | interactive label picker missing labels |
| \`modules/task/pull_review_comment.go\` | \`ListPullReviews\` | review comments truncated |
| \`modules/task/login_ssh.go\` | \`ListMyPublicKeys\` | SSH key auto-detection fails |
| \`modules/task/login_create.go\` | \`ListAccessTokens\` | token name deduplication misses existing tokens |
| \`cmd/pulls.go\` | \`ListPullReviews\` | PR detail view missing reviews |
| \`cmd/releases/utils.go\` | \`ListReleases\` | tag lookup fails on repos with many releases |
| \`cmd/attachments/delete.go\` | \`ListReleaseAttachments\` | attachment deletion fails when many attachments exist |
## Fix
Each call site is replaced with an explicit pagination loop that follows \`resp.NextPage\` until all pages are exhausted.
Reviewed-on: https://gitea.com/gitea/tea/pulls/967
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Alain Thiffault <athiffau@effectivemomentum.com>
Co-committed-by: Alain Thiffault <athiffau@effectivemomentum.com>
## Summary
- Add `"ci"` as a new selectable field for `tea pr list --fields`, allowing users to see CI status across multiple PRs at a glance
- Fetch CI status via `GetCombinedStatus` API **only when the `ci` field is explicitly requested** via `--fields`, avoiding unnecessary API calls in default usage
- Improve CI status display in both detail and list views:
- **Detail view** (`tea pr <index>`): show each CI check with symbol, context name, description, and clickable link to CI run
- **List view** (`tea pr list --fields ci`): show symbol + context name per CI check (e.g., `✓ lint, ⏳ build, ❌ test`)
- **Machine-readable output**: return raw state string (e.g., `success`, `pending`)
- Replace pending CI symbol from `⭮` to `⏳` for better readability
- Extract `formatCIStatus` helper and reuse it in `PullDetails` to reduce code duplication
- Add comprehensive tests for CI status formatting and PR list integration
## Detail View Example
```
- CI:
- ✓ [**lint**](https://ci.example.com/lint): Lint passed
- ⏳ [**build**](https://ci.example.com/build): Build is running
- ❌ [**test**](https://ci.example.com/test): 3 tests failed
```
## List View Example
```
INDEX TITLE STATE CI
123 Fix bug open ✓ lint, ⏳ build, ❌ test
```
## Usage
```bash
# Show CI status column in list
tea pr list --fields index,title,state,ci
# Default output is unchanged (no CI column, no extra API calls)
tea pr list
```
## Files Changed
- `cmd/pulls/list.go` — conditionally fetch CI status per PR when `ci` field is selected
- `modules/print/pull.go` — add `ci` field, `formatCIStatus` helper, improve detail/list CI display
- `modules/print/pull_test.go` — comprehensive tests for CI status formatting
## Test plan
- [x] `go build ./...` passes
- [x] `go test ./...` passes (11 new tests)
- [x] `tea pr list` — default output unchanged, no extra API calls
- [x] `tea pr list --fields index,title,state,ci` — CI column with context names
- [x] `tea pr <index>` — CI section shows each check with name, description, and link
- [x] `tea pr list --fields ci -o csv` — machine-readable output shows raw state strings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: https://gitea.com/gitea/tea/pulls/956
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
## Summary
- Extract duplicate \`getReleaseByTag\` into shared \`cmd/releases/utils.go\`
- Replace \`log.Fatal\` calls with proper error returns in config and login commands; \`GetLoginByToken\`/\`GetLoginsByHost\`/\`GetLoginByHost\` now return errors
- Remove dead \`portChan\` channel in \`modules/auth/oauth.go\`
- Fix YAML integer detection to use \`strconv.ParseInt\` (correctly handles negatives and large ints)
- Fix \`path.go\` error handling to use \`errors.As\` + \`syscall.ENOTDIR\` instead of string comparison
- Extract repeated credential helper key into local variable in \`SetupHelper\`
- Use existing \`isRemoteDeleted()\` in \`pull_clean.go\` instead of duplicating the logic
- Fix ~30 error message casing violations to follow Go conventions
- Use \`fmt.Errorf\` consistently instead of string concatenation in \`generic.go\`
Reviewed-on: https://gitea.com/gitea/tea/pulls/947
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Bo-Yi Wu (吳柏毅) <appleboy.tw@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-committed-by: Nicolas <bircni@icloud.com>
- Add an owner flag to the repos list command to list repositories for a specific user or organization
- Implement owner-based repository listing by detecting whether the owner is an organization or a user and calling the appropriate API
- Improve error handling for owner lookup by checking HTTP status codes instead of relying on error string matching
- Align repository search logic with the updated owner lookup behavior using HTTP response validation
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/931
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
## Summary
- Add `tea repo edit` subcommand to update repository properties via the Gitea API
- Support flags: `--name`, `--description`/`--desc`, `--website`, `--private`, `--template`, `--archived`, `--default-branch`
- Boolean-like flags use string type to distinguish "not set" from "false", following the pattern in `releases/edit.go`
## Test plan
- [x] `go build ./...` passes
- [x] `go vet ./...` passes
- [x] `tea repo edit --help` shows all flags correctly
- [x] Manual test: `tea repo edit --private true` on a test repo
- [x] Manual test: `tea repo edit --name new-name --description "new desc"` on a test repo
Reviewed-on: https://gitea.com/gitea/tea/pulls/928
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
## Summary
- Introduce `github.com/go-authgate/sdk-go/credstore` to store OAuth tokens securely in the OS keyring (macOS Keychain / Linux Secret Service / Windows Credential Manager), with automatic fallback to an encrypted JSON file
- Add `AuthMethod` field to `Login` struct; new OAuth logins are marked `auth_method: oauth` and no longer write `token`/`refresh_token`/`token_expiry` to `config.yml`
- Add `GetAccessToken()` / `GetRefreshToken()` / `GetTokenExpiry()` accessors that transparently read from credstore for OAuth logins, with fallback to YAML fields for legacy logins
- Update all token reference sites across the codebase to use the new accessors
- Non-OAuth logins (token, SSH) are completely unaffected; no migration of existing tokens
## Key files
| File | Role |
|------|------|
| `modules/config/credstore.go` | **New** — credstore wrapper (Load/Save/Delete) |
| `modules/config/login.go` | Login struct, token accessors, refresh logic |
| `modules/auth/oauth.go` | OAuth flow, token creation / re-authentication |
| `modules/api/client.go`, `cmd/login/helper.go`, `cmd/login/oauth_refresh.go` | Token reference updates |
| `modules/task/pull_*.go`, `modules/task/repo_clone.go` | Git operation token reference updates |
## Test plan
- [x] `go build ./...` compiles successfully
- [x] `go test ./...` all tests pass
- [x] `tea login add --oauth` completes OAuth flow; verify config.yml has `auth_method: oauth` but no token/refresh_token/token_expiry
- [x] `tea repos ls` API calls work (token read from credstore)
- [x] `tea login delete <name>` credstore token is also removed
- [x] Existing non-OAuth logins continue to work unchanged
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: https://gitea.com/gitea/tea/pulls/926
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
- switch to golangci-lint for linting
- switch to gofmpt for formatting
- fix lint and fmt issues that came up from switch to new tools
- upgrade go-sdk to 0.23.2
- support pagination for listing tracked times
- remove `FixPullHeadSha` workaround (upstream fix has been merged for 5+ years at this point)
- standardize on US spelling (previously a mix of US&UK spelling)
- remove some unused code
- reduce some duplication in parsing state and issue type
- reduce some duplication in reading input for secrets and variables
- reduce some duplication with PR Review code
- report error for when yaml parsing fails
- various other misc cleanup
Reviewed-on: https://gitea.com/gitea/tea/pulls/869
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-committed-by: techknowlogick <techknowlogick@gitea.com>
## Summary
Fix the `tea labels delete` and `tea labels update` commands which were silently ignoring the `--id` flag.
## Problem
Both commands used `IntFlag` for the `--id` parameter but called `ctx.Int64("id")` to retrieve the value. This type mismatch caused the ID to always be read as `0`, making the commands useless.
**Before (bug):**
```bash
$ tea labels delete --id 36 --debug
DELETE: .../labels/0 # Wrong! ID ignored
```
**After (fix):**
```bash
$ tea labels delete --id 36 --debug
GET: .../labels/36 # Verify exists
DELETE: .../labels/36 # Correct ID
Label 'my-label' (id: 36) deleted successfully
```
## Changes
### labels/delete.go
- Change `IntFlag` to `Int64Flag` to match `ctx.Int64()` usage
- Make `--id` flag required
- Verify label exists before attempting deletion
- Provide clear error messages with label name and ID context
- Print success message after deletion
### labels/update.go
- Change `IntFlag` to `Int64Flag` to fix the same bug
## Test plan
- [x] `go test ./...` passes
- [x] `go vet ./...` passes
- [x] `gofmt` check passes
- [x] Manual testing confirms ID is now correctly passed to API
- [ ] CI passes
Reviewed-on: https://gitea.com/gitea/tea/pulls/865
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Alain Thiffault <athiffau@effectivemomentum.com>
Co-committed-by: Alain Thiffault <athiffau@effectivemomentum.com>
This change enhances the 'issue' command functionality by enabling structured JSON
output for single issue views and introducing a method for output redirection.
**Changes Implemented:**
1. Enables the existing `--output json` flag for single issue commands (e.g., 'tea issue 17'). This flag was previously ignored in this context.
2. Introduces the new `--out <filename>` flag, which redirects the marshaled JSON output from stdout to the specified file.
Feeback more then welcome.
Co-authored-by: Jonas Toth <development@jonas-toth.eu>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/841
Reviewed-by: TheFox0x7 <thefox0x7@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Riccardo Förster <riccardo.foerster@sarad.de>
Co-committed-by: Riccardo Förster <riccardo.foerster@sarad.de>
## Problem
Tea commands fail when run from git worktrees with the error:
Remote repository required: Specify ID via --repo or execute from a
local git repo.
Even though the worktree is in a valid git repository with remotes
configured.
Additionally, `tea pr create` was missing context validation, showing
cryptic errors like `"path segment [0]
is empty"` instead of helpful messages.
## Root Cause
1. **Worktree issue**: go-git's `PlainOpenWithOptions` was not
configured to read the `commondir` file that
git worktrees use. This file points to the main repository's `.git`
directory where remotes are actually
stored (worktrees don't have their own remotes).
2. **PR create issue**: Missing `ctx.Ensure()` validation meant errors
weren't caught early with clear
messages.
## Solution
### 1. Enable worktree support (`modules/git/repo.go`)
```go
EnableDotGitCommonDir: true, // Enable commondir support for worktrees
This tells go-git to:
- Read the commondir file in .git/worktrees/<name>/commondir
- Follow the reference (typically ../..) to the main repository
- Load remotes from the main repo's config
2. Add context validation (cmd/pulls/create.go)
ctx.Ensure(context.CtxRequirement{
LocalRepo: true,
RemoteRepo: true,
})
Provides clear error messages and matches the pattern used in pr
checkout (fixed in commit 0970b945 from
2020).
3. Add test coverage (modules/git/repo_test.go)
- Creates a real git repository with a worktree
- Verifies that RepoFromPath() can open the worktree
- Confirms that Config() correctly reads remotes from main repo
Test Results
Without fix:
❌ FAIL: Should NOT be empty, but was map[]
With fix:
✅ PASS: TestRepoFromPath_Worktree (0.12s)
Manual test in worktree:
cd /path/to/worktree
tea pr create --title "test"
# Now works! ✅
Checklist
- Tested manually in a git worktree
- Added test case that fails without the fix
- All existing tests pass
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/850
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: Brandon Martin <brandon@codedmart.com>
Co-committed-by: Brandon Martin <brandon@codedmart.com>