# Git for WASI 0.2 This crate adapts the v2 Git plugin's local operations, HTTP transport, and SSH transport. It uses gix 0.90 and the patches in [`vendor/PATCHES.md`](vendor/PATCHES.md). It does not use the plugin SDK, RPC dispatch, the vault, or native child processes. The host supplies HTTP credentials and SSH signatures through standard `wasi:http`. SSH clone, fetch, and push use the shared [`ssh-client`](../ssh-client/README.md) library in-process. The remote command runs `git-upload-pack` or `git-receive-pack` over standard WASI sockets. Remote URLs accept `ssh://user@host[:port]/path` and `user@host:path`. SCP-style URLs use port 22. The user can come from the URL or the host identity. The host key must already match `$HOME/.ssh/known_hosts`. Use the `ssh` CLI with `-o StrictHostKeyChecking=accept-new` to record a new key. See [`../ssh/README.md`](../ssh/README.md) for the native sshd fixture and `ssh-test.nu` commands. ## Build Run the build from `userland/` to include the workspace's cwd constructor: ```sh CARGO_TARGET_DIR=/tmp/p1-git/target nix develop .. -c cargo build -p git --release ``` The following commands run from the repository root. ## Native/WASI comparison and import check ```sh nix develop -c nu userland/git/test.nu /tmp/p1-git/target/wasm32-wasip2/release/git.wasm /tmp/p1-git/server-target/debug/git-test-http-server ``` Build the native HTTP fixture first with the command in the local smart-HTTP section. The comparison script starts the fixture on an ephemeral port. It stops the fixture after success or failure. The script checks that the component imports only WASI interfaces. It creates separate native and WASI repositories under `/tmp/p1-git/`. It fixes both identities and timestamps. It compares stdout and exit codes without filtering command output. The initialization check normalizes only its repository path. Worktree listings compare exit codes only because their paths differ. Mutation checks also compare repository state through native Git on both fixtures. They compare status, commit IDs, parents, authors, diffs, and references. The script retains its fixtures for inspection. `wasmtime run` needs `-S http`, including for local operations. It also needs `-S cli-exit-with-code=y` to preserve Git's numeric exit codes. The component uses WASI's `exit-with-code`, not a custom host import. The world host must enable that optional WASI function. The tests pass explicit `-C` paths. ## Identity The host creates `/etc/gitconfig` when it is absent. It sets `user.name = Sausage` and `user.email = sausage@localhost`. Git reads this system configuration unless `GIT_CONFIG_NOSYSTEM` disables it. Local repository configuration overrides these defaults. The host preserves an existing system configuration. The `Sausage-Conversation` trailer retains conversation attribution. Reflog-only operations use a fallback committer when no identity exists. They include reset, checkout, branch changes, and reference updates. New commits and annotated tags still require an identity. ## Agent command forms The comparison suite covers per-command `-c key=value` overrides and quiet local commands. It checks multiple revisions, detached status, branch deletion messages, and merge commit output. It also checks these forms: - `status -sb` and `status --porcelain=v1`. - `log -N`, `log -n N`, `--max-count=N`, `--oneline`, `-p`, `--stat`, and one literal path after `--`. - `log --format` and `--pretty=format:` with `%H %h %s %an %ae %ad %d`. - `log --decorate` and `--decorate=short`, including `--oneline`. - `show -s --format=...` and `show -s --pretty=format:...`. - `%P %p %T %t %cn %ce %cd %ar %cr %b %B %n` in log and show formats. - `log A..B`, `--name-only`, `--no-renames`, and `--diff-merges=first-parent`. - `%x00`, `%x1e`, and `%(trailers:key=Sausage-Conversation,valueonly)` in log formats. - `show --stat`, `--oneline`, `--name-only`, `show HEAD:path`, and merge commits without a combined diff. - `cat-file -t`, `-p`, `-s`, and `-e` for object inspection. - `ls-tree [-r] [--name-only] [path]` for literal paths. - `archive [--format=tar] [-o ] [path]` for a tree or literal subtree. The archive preserves file contents, executable modes, and symbolic links. `archive-test.nu` compares extracted output with native Git. - `diff HEAD~1`, `diff A..B`, `--name-only`, `--name-status`, and one literal path after `--`. - `diff --check` and `diff --cached --check` for added whitespace errors and conflict markers. - `branch -a`, `-d`, `-D`, `-f`, `-m`, and `--show-current`. - `checkout -b`, `switch -c`, and `merge --no-ff`. - `rev-parse --short HEAD`, `ls-files`, and `config --get`. - Quiet `init`, `commit`, branch deletion, `checkout`, `switch`, `merge`, and `rebase`. - `init` in an existing repository without replacement of HEAD or configuration. - `reset --soft`, `reset --hard`, mixed `reset HEAD~1`, and `reset HEAD `. - `commit --amend -m`, `commit --amend --no-edit`, and `commit -F`. - `checkout `, `checkout -- `, `restore `, and `restore --staged`. - Bare `stash`, `stash push`, `-m`, `list`, `pop`, `apply`, and `drop`. - Quiet stash push, pop, and drop. - Lightweight tags, `tag -a -m`, bare `tag`, `--list`, and `-d`. - `remote -v`, `add`, `remove`, `set-url`, and `get-url`. - Clean `cherry-pick` and conflict resolution through `--continue` or `--abort`. - `merge-base A B`, `--is-ancestor`, and `rev-list --count A..B`. - `rev-parse --verify --quiet`, `--abbrev-ref HEAD`, and `symbolic-ref [--quiet] [--short] HEAD`. - `update-ref []` with an atomic old-value check. - `worktree add --detach `, `prune`, and `list --porcelain`. - `checkout --detach --force ` and `clean -fdq`. - `merge --no-edit -m ` and `--ff-only`. - `diff --numstat --no-renames`, `--diff-filter=U --name-only`, and `--name-only A B`. - NUL-separated status, file lists, diff names, and numstat through `-z`. - `status --porcelain --untracked-files=all` and `ls-files --others --exclude-standard`. - `branch --list ` and `check-ignore --no-index `. - `fetch ` with the selected revision in `FETCH_HEAD`. If `SAUSAGE_CONVERSATION` exists, `commit` appends its value as a `Sausage-Conversation` trailer. The trailer follows a blank line after the message body. Without this variable, `commit` preserves the normal message. The tests compare both forms with native Git, including commit IDs. `clean -fdq` preserves tracked files, ignored files, and nested repositories. CLI `status` refreshes cached file stats in the index after the content comparison. Repeated status calls reuse those stats instead of hashing unchanged files again. If the index cannot accept writes, status still reports changes. The read-only status API does not refresh the index. The comparison suite checks cache refresh, cache reuse, and same-size content edits. The log applies its path filter before its count limit. Stashes use ordinary Git commits and the `refs/stash` reflog. Stash accepts only the listed subcommands. Cherry-pick conflicts use `CHERRY_PICK_HEAD` and `MERGE_MSG`. ### Unsupported forms These forms return a usage error with exit code 129 and empty stdout. The suite checks the usage error instead of native Git's successful operation. - `log --graph --oneline --all`. - `show` of a merge that changes the same path relative to every parent, which requires a combined diff. The CLI supports textual diffs and literal paths, not Git's complete pathspec language. The format options support the placeholders above, not Git's complete pretty-format language. The suite compares stdout and exit codes. It does not compare stderr text. ## Local smart-HTTP test Build the native fixture server: ```sh CARGO_TARGET_DIR=/tmp/p1-git/server-target nix develop -c cargo build --manifest-path userland/git/tests/http-server/Cargo.toml ``` The server wraps native `git http-backend` without TLS. It accepts a repository directory and a readiness file. The readiness file receives the bound localhost port. Start the server in a separate terminal: ```sh mkdir -p /tmp/p1-git/http-repos /tmp/p1-git/server-target/debug/git-test-http-server /tmp/p1-git/http-repos /tmp/p1-git/http-port ``` Use that port in the integration test: ```sh nix develop -c nu userland/git/http-test.nu /tmp/p1-git/target/wasm32-wasip2/release/git.wasm /tmp/p1-git/http-repos http://127.0.0.1:PORT/repo.git ``` Use a fresh repository name for each test run. The script checks anonymous clone, fetch, and push through WASI. It checks the resulting object IDs with native Git. ## Anonymous HTTPS test The host handles TLS, not the guest. Test HTTPS separately against the small public Hello-World repository: ```sh nix develop -c nu userland/git/public-https-test.nu /tmp/p1-git/target/wasm32-wasip2/release/git.wasm ``` This test clones the repository and fetches it again. It does not need credentials. HTTPS push requires the host's later credential injection.