g1t/apps/docs/src/content/docs/guides/git.md

216 lines8,621 bytesCodeBlame
1---
2title: Git
3description: Remotes, credentials, private repositories and limits.
4---
5
6g1t speaks git's smart HTTP protocol. Any git client works.
7
8## Remotes
9
10```text
11https://g1t.sh/<workspace>/<repo>.git
12```
13
14Public repositories can be cloned without signing in:
15
16```sh
17git clone https://g1t.sh/flagon-io/g1t.git
18```
19
20If the workspace is [renamed](/guides/workspaces/#rename-a-workspace), the
21old remote redirects to the new one for 90 days. Git follows the redirect
22and warns about it; point the remote at the new address:
23
24```sh
25git remote set-url origin https://g1t.sh/<new-workspace>/<repo>.git
26```
27
28## Authentication
29
30Pushing, and reading private repositories, needs credentials. Use your
31username, and as the password either your account password or an
32[access token](https://g1t.sh/settings/tokens). Tokens are recommended: they can be revoked
33individually and they also work for the API.
34
35To avoid typing it each time, let git store it:
36
37```sh
38git config --global credential.helper store
39```
40
41## Creating a repository by pushing
42
43Pushing to a repository that does not exist, in a workspace you belong to,
44creates it as a private repository, so nothing pushed by mistake is
45published. To make it public, see
46[change who can see a repository](/guides/managing-repositories/#change-who-can-see-a-repository).
47
48```sh
49git push https://g1t.sh/<workspace>/new-repo.git main
50```
51
52## Private repositories
53
54A private repository is visible only to people with a
55[role](/guides/access-and-roles/) on it. Cloning and fetching need
56Read, and pushing needs Write. To
57everyone else it looks exactly like a repository that does not exist, both
58on the site and to git.
59
60On the site, an address you cannot see gives the same page either way, with
61status 404:
62
63| You are | The page says |
64| --- | --- |
65| Signed out | **Nothing here**: this page doesn't exist, or it's private; sign in if it's yours. **Sign in** brings you back to the same address. |
66| Signed in | **Nothing here**: this page doesn't exist, or you don't have access to it, with which account you are signed in as and a link to switch account. If you should have access, ask someone with the Admin role on it to add you. |
67
68Issues, pull requests and workspace pages work the same way. The sidebar
69does not open the project or workspace the address names, so nothing on
70the page hints at whether it exists; a missing file, commit or issue in a
71project you can see keeps that project's sidebar. A profile that does not
72exist says **No one on g1t goes by that name**, since profiles are public.
73
74## Browsing without an account
75
76Public projects, Explore, Search and profiles are open to everyone, in the
77same sidebar members use. Signed out, the sidebar has Explore and Search,
78and in a project its Code, Issues, Pull requests, Agents, Workflows and
79Deployments; pages only people with a role on the repository see, such as
80Security and Settings, are left out. **Sign in** and **Sign up** sit at the bottom, and both bring you
81back to the page you were on.
82
83## Protected branches
84
85A repository can protect its default branch under **Settings → Branches and
86merging**. Pushing to it is then refused for everyone, whatever their role, and for agents, and
87git says why:
88
89```text
90 ! [remote rejected] main -> main (main is protected: push a branch and open a pull request)
91```
92
93Changes reach a protected branch only by merging a pull request. The first
94push to an empty repository is still allowed.
95
96The same page sets what a merge needs: the
97[required status checks](/guides/pull-requests/#required-status-checks)
98and approvals.
99
100## Branches
101
102Push any branch to a repository you can write to, and open a
103[pull request](/concepts/overview/#pull-requests) from it on the
104repository's **Pull requests** tab.
105
106```sh
107git switch -c my-change
108git push origin my-change
109```
110
111## Pull request forks
112
113A pull request that was not opened from a branch has its own remote:
114
115```text
116https://g1t.sh/pulls/<pull request id>.git
117```
118
119Only whoever opened the pull request can push to it, or, for one g1t
120made, whoever asked for it. Pushes to a fork
121update the pull request's head commit on its page.
122
123## Limits
124
125### Size limits
126
127Repositories are stored in Cloudflare Artifacts. g1t checks its limits
128before a push is stored, and declines a push that would cross one. git
129prints the reason beside each branch (`! [remote rejected] main (…)`), and
130what to do as `remote:` lines. Nothing in a declined push is stored.
131
132| Limit | Size | What happens past it |
133| --- | --- | --- |
134| A file | 32 MB | The push is declined, naming the file's size. |
135| A repository, with its pull requests' forks | 950 MB, as g1t counts what was pushed (the store holds 1 GB) | The push is declined; once full, pushes are refused with the reason before any data is sent. |
136| A push that push protection can scan before it lands | Most pushes; very large ones are scanned after they land | A very large push goes through and is scanned after it lands; secrets found are open alerts. To have it checked first, push in parts, oldest commits first. |
137| A push | 100 MB | Refused by the network with HTTP `413` before g1t sees it. |
138
139To push a large history in parts:
140
141```sh
142git rev-list --reverse HEAD | awk 'NR % 500 == 0' | xargs -I{} git push origin {}:refs/heads/main
143git push origin main
144```
145
146Each push sends only what the one before did not.
147
148### When the store is busy
149
150If Cloudflare Artifacts is rate limiting g1t or not answering, g1t tries
151reads again for a moment, then answers git with HTTP `429` (rate limited)
152or `503` (unavailable) and a `Retry-After` header saying how many seconds
153to wait. Pushes are never tried again on your behalf: run `git push`
154again. On g1t.sh the page says the git storage is busy instead of failing,
155and [status.g1t.sh](https://status.g1t.sh) shows **Git storage**.
156
157### Git operations
158
159Each clone, fetch and push is a git operation. Every workspace has 50,000
160a month included. Past that, a workspace on the g1t plan pays $0.18 per
1611,000, and a free workspace is never charged: past 50,000 in a month, its
162git requests past 60 in an hour are answered `429` with when to try again,
163until the month turns. Counting starts on 2026-10-14. See
164[git operations](/guides/usage-and-billing/#git-operations).
165
166What these limits mean in practice, and what to do instead, is on
167[What g1t can't do yet](/about/limitations/#git).
168
169## Where a slow request's time went
170
171Every answer g1t gives git carries a `Server-Timing` header: how many
172milliseconds each step of the request took. To see it, run git with its
173HTTP trace on:
174
175```sh
176GIT_TRACE_CURL=1 git ls-remote https://g1t.sh/<owner>/<repo>.git 2>&1 | grep -i server-timing
177```
178
179| Step | What it is |
180| --- | --- |
181| `repo` | Finding the repository, and checking your credentials if you sent any |
182| `moved` | Only for an address with no repository: looking for a renamed workspace or a transferred repository to send you to |
183| `access` | Deciding whether you may fetch from or push to it |
184| `kept` | A free workspace's limits, and looking for a ref listing and a store credential made a moment ago |
185| `mint` | Only when no credential was kept: the git store making one for the request |
186| `store` | The git store's answer; for a push, checking it for secrets first |
187| `refs` | Only for a push: recording that the repository's refs changed |
188| `total` | Everything g1t did |
189| `repos` | The same, measured where your request arrived |
190
191Two entries say how a step went rather than how long it took:
192
193| Entry | Values |
194| --- | --- |
195| `refs;desc=` | `hit-colo` or `hit-shared` when the ref listing came from g1t's cache, `miss` when the git store was asked |
196| `cred;desc=` | `isolate` or `shared` for a store credential made a moment ago, `mint` for a new one |
197
198The ref listing git asks for first on every clone and fetch is kept for up
199to a minute, and only the same question about the same refs gets the same
200answer: a push, a merge or any other change to a repository's branches and
201tags makes the next fetch ask the git store again. A change can take up to
2025 seconds to reach every fetch.
203
204Include the header when you report a slow clone, fetch or push.
205
206## SSH
207
208Git over SSH is not available yet. Use HTTPS, which works for clone,
209fetch and push everywhere SSH would.
210
211Why: git over SSH needs raw TCP connections on port 22, and g1t runs
212entirely on Cloudflare's network. Accepting inbound TCP traffic directly
213into Workers is in a beta from Cloudflare that g1t has applied for and is
214waiting on. SSH keys can already be added under
215[Settings → SSH keys](https://g1t.sh/settings/keys),
216and will be used once SSH is on.