---
id: f16cf2b4-6ebf-4fbf-b78f-2550f44bd76b
title: Export duplicates the git runner, shares the 10s timeout, and round-trips paths through UTF-8
state: closed
labels:
- refactor
assignees: []
milestone: null
created: 2026-10-27T14:47:57.173716686Z
updated: 2026-10-27T15:53:18.730189081Z
---
`export_dumb_repo` re-implements the git subprocess plumbing (~75 lines) instead of reusing `run_git`, and runs the export under the same 10 s `GIT_TIMEOUT`.

Problems:

1. **Second full traversal under a 10 s cap.** `snapshot()` already reads the whole history for `git bundle create` under `GIT_TIMEOUT`. The export is a *second* full pass with the same 10 s budget, so a repo whose bundle takes ~7 s now exceeds the cap in the export step and fails the build with `GitError::Timeout("git clone/update-server-info")` — a regression for large repos that deployed fine before #54.
2. **Duplicated runner.** `run_plain_git` copies `run_git`'s spawn / stdout-stderr reader threads / poll loop / kill-on-timeout. Every future fix must be made twice; the two already differ in error text and in the `--git-dir` handling.
3. **Lossy path round-trip.** `canonicalize()` -> `to_string_lossy()` -> `OsString` converts non-UTF-8 paths into replacement characters, so the clone target can be a *different* path than intended.

Fix:
- fold the runner into one helper that takes an optional cwd (`git -C <cwd>` vs plain `git`), keeping `GIT_TIMEOUT` and the kill/reader-thread behaviour identical
- pass `&OsStr`/`Path` straight through (no string conversion)
- once #55/#56 are fixed (export from the existing bundle) the marginal cost is small; still, give the export its own, larger budget rather than sharing 10 s with a full-history read

Acceptance:
- [ ] one subprocess helper; `run_plain_git` gone
- [ ] no `to_string_lossy` on paths handed to git
- [ ] timeout is not silently doubled against the same cap (documented constant or separate budget)
