| 1 | --- |
| 2 | title: Repositories |
| 3 | description: Create, fork, import, and manage repositories over the typed REST plane. |
| 4 | --- |
| 5 | |
| 6 | Repositories live at `/api/v1/repos`, declared as an Effect `HttpApi`. |
| 7 | One schema types the server, the client, and your tests: |
| 8 | |
| 9 | ```typescript |
| 10 | import { GitApi } from "alchemy/Git"; |
| 11 | import * as HttpApiClient from "effect/http-api/HttpApiClient"; |
| 12 | |
| 13 | const client = yield* HttpApiClient.make(GitApi, { baseUrl: host }); |
| 14 | |
| 15 | const created = yield* client.repos.create({ |
| 16 | payload: { owner: "acme", name: "web", public: true }, |
| 17 | }); |
| 18 | ``` |
| 19 | |
| 20 | Everything below is also plain HTTP for `curl` and `fetch`, and the |
| 21 | [GitHub facade](/git/github-api) covers Octokit. |
| 22 | |
| 23 | ## Creating |
| 24 | |
| 25 | ```typescript |
| 26 | yield* client.repos.create({ |
| 27 | payload: { |
| 28 | owner: "acme", |
| 29 | name: "web", |
| 30 | description: "the marketing site", |
| 31 | defaultBranch: "main", // default |
| 32 | public: true, // default false |
| 33 | readOnly: false, |
| 34 | }, |
| 35 | }); |
| 36 | ``` |
| 37 | |
| 38 | The response carries the repository and its clone URL. A duplicate |
| 39 | `owner/name` is a typed `RepoAlreadyExists`. Who may create under an |
| 40 | owner is your middleware's call; the engine attaches no meaning to the |
| 41 | owner name beyond uniqueness. |
| 42 | |
| 43 | `owner/name` is the public identity. The repository's id is stable, so |
| 44 | renaming never moves data. |
| 45 | |
| 46 | ## Visibility |
| 47 | |
| 48 | `public` is a flag the engine stores and reports. What it grants is |
| 49 | your middleware's decision; the tutorial's middleware serves anonymous |
| 50 | reads of a public repository, REST reads, raw files, the ref |
| 51 | advertisement, and `upload-pack`, and answers `401` with |
| 52 | `WWW-Authenticate` otherwise, so git prompts. `Git.isRead` and |
| 53 | `Git.RegistryStore` are what it asks. |
| 54 | |
| 55 | Listings return everything the Registry holds; `public: true` in the |
| 56 | query narrows them to public repositories. |
| 57 | |
| 58 | ## Read-only |
| 59 | |
| 60 | `readOnly: true` rejects pushes in-band, so the client sees a clear |
| 61 | error on the ref rather than a transport failure. Use it to freeze a |
| 62 | migrated repository or park one you are retiring. |
| 63 | |
| 64 | ## Updating |
| 65 | |
| 66 | ```typescript |
| 67 | yield* client.repos.update({ |
| 68 | params: { owner: "acme", repo: "web" }, |
| 69 | payload: { description: "archived", readOnly: true }, |
| 70 | }); |
| 71 | ``` |
| 72 | |
| 73 | `defaultBranch` must name a branch that exists. Pointing it at a |
| 74 | missing ref is a typed `RefNotFound`. |
| 75 | |
| 76 | ## Forking and importing |
| 77 | |
| 78 | Fork copies a repository server-side, with no round trip through a |
| 79 | client: |
| 80 | |
| 81 | ```typescript |
| 82 | yield* client.repos.fork({ |
| 83 | params: { owner: "acme", repo: "web" }, |
| 84 | payload: { targetOwner: "dana", targetName: "web" }, |
| 85 | }); |
| 86 | ``` |
| 87 | |
| 88 | Import pulls from any public git URL. Both return at once with |
| 89 | `status: "importing"`. Poll `repos.get` until it reads `"ready"`. |
| 90 | |
| 91 | ## Deleting |
| 92 | |
| 93 | ```typescript |
| 94 | yield* client.repos.delete({ params: { owner: "acme", repo: "web" } }); |
| 95 | ``` |
| 96 | |
| 97 | Delete answers `204` at once and purges in the background. The name is |
| 98 | released and the objects drain through a scheduled job. A second |
| 99 | delete during the drain is an idempotent `204`, and reads of a |
| 100 | draining repository behave as if it were gone. |
| 101 | |
| 102 | ## Listing |
| 103 | |
| 104 | ```typescript |
| 105 | const page = yield* client.repos.list({ query: { owner: "acme" } }); |
| 106 | ``` |
| 107 | |
| 108 | Paginated with an opaque cursor. Listings come from the |
| 109 | [registry](/git/blocks/registry), so object counts read as zero here. |
| 110 | Fetch a repository directly for live numbers. |