# Summary of Changes

All tests pass. Here's the summary:

**Bug**: When `git-sync sync` is run against a public source repo (e.g. `https://gitlab.baylibre.com/...`), it would interactively prompt for username/password on a host the user had never authenticated against.

**Root cause** (`internal/auth/auth.go:69`): `git credential fill` falls back to an interactive terminal prompt on `/dev/tty` when no configured helper has credentials. `git-sync` is non-interactive, so this prompt shouldn't happen — anonymous access should be attempted instead.

**Fix**: Set `GIT_TERMINAL_PROMPT=0` on the `git credential fill` invocation. With this, when no helper has credentials, git exits non-zero, our existing code treats that as "no credentials found", and we proceed anonymously. I also extracted `newCredentialFillCmd` so the env-var contract is unit-testable (`TestCredentialFillCmdDisablesTerminalPrompt`).

# Proposed Options
So you have two options:

**Option A — ship the current fix.** Bug #63 (interactive prompt) is fixed. Cost: we still call `git credential fill` proactively, so if you have stored credentials for a host, we'll attach them on requests that didn't need auth. For most users this is harmless, but it does mean we send a token to a public repo we didn't need to.

**Option B — match git's actual behaviour.** Change `Resolve()` to skip the helper lookup, then make `HTTPConn` retry on 401 by consulting the helper at that point. Outline:

- `auth.Resolve()` returns nil when only the helper would have credentials (don't call it eagerly)
- Add a `CredentialProvider` callback on `HTTPConn` that calls `git credential fill`
- In `RequestInfoRefs` and `PostRPC`, on a 401 response, call the provider, retry once with the returned creds
- On retry success, also call `git credential approve`; on persistent 401, `git credential reject`

That's roughly a 100-line change touching `auth/`, `gitproto/smarthttp.go`, and a few tests. Slightly more if we want to handle 401s mid-stream during the pack post (which we should, since some servers only challenge on `git-upload-pack` not `/info/refs`).

# User Discussion
My instinct: the bug filer just wants the prompt to stop. Option A solves that today. Option B is the better long-term shape and worth filing as a follow-up, but I'd rather propose it cleanly than wedge it into this PR. Want me to:
1. Ship Option A and open a follow-up issue for Option B, or
2. Do Option B now as part of this fix?

# Behavior Changes
Here's the summary of what we built:

**Behaviour change**: `git-sync` now matches git's own HTTP auth behaviour — try anonymous first, only consult the credential helper on a 401 response from the server.

**Files changed**:

- `internal/auth/auth.go` — `Resolve()` no longer eagerly calls `git credential fill`. New `GitCredentialHelper` type with `Lookup`/`Approve`/`Reject` methods that shell out to `git credential` with `GIT_TERMINAL_PROMPT=0` so it never prompts.
- `internal/gitproto/smarthttp.go` — New `CredentialHelper` interface on `HTTPConn`. `RequestInfoRefs` retries once on 401, stores resolved auth for subsequent calls (e.g. `PostRPC`), and signals approve/reject back to the helper.
- `internal/syncer/syncer.go` — Wires `auth.GitCredentialHelper{}` onto HTTP conns when no explicit auth was configured.
- Tests: 7 new HTTPConn 401-retry tests + 8 new auth helper tests + updated `TestResolve` to assert the helper is never consulted up-front. The pre-existing `TestRun_IntegrationUsesGitCredentialHelperFallback` end-to-end test still passes — it now exercises the new 401-retry path against a real `httptest` server.

The net effect: For the user in issue #63, syncing from a public GitLab repo now works without prompting — git-sync makes the request anonymously, the server returns 200, no helper is ever consulted. For users with private repos and a configured helper, the helper is consulted only when needed.

# Further Considerations
Here’s one thing we could add here: Let's say I talked to a host that needs credentials and I enter the right ones, then it's saved in the helper. If they expire, the next time I run git-sync I get a 403.

## Implementation Plan
Where to wire it in git-sync:
- lookupGitCredential returns a func() error (the "reject closure") alongside the username/password, capturing the input it sent to fill
- The session-level code wraps the AuthMethod with that closure
- On 401, the session calls the closure (which runs git credential reject for the same input).

# Conclusion
In summary, based on the detailed review and the changes suggested, I will go ahead and create a PR for #63 now, as proposed.

### Additional Cleanup Summary
- Dropped duplicate struct; use utility directly in the retry path
- Enhanced approval/rejection methods
- Introduced helper to consolidate tests and reduce redundant logic
