| 1 | --- |
| 2 | title: Git |
| 3 | description: Remotes, credentials, private repositories and limits. |
| 4 | --- |
| 5 | |
| 6 | g1t speaks git's smart HTTP protocol. Any git client works. |
| 7 | |
| 8 | ## Remotes |
| 9 | |
| 10 | ```text |
| 11 | https://g1t.sh/<workspace>/<repo>.git |
| 12 | ``` |
| 13 | |
| 14 | Public repositories can be cloned without signing in: |
| 15 | |
| 16 | ```sh |
| 17 | git clone https://g1t.sh/flagon-io/g1t.git |
| 18 | ``` |
| 19 | |
| 20 | If the workspace is [renamed](/guides/workspaces/#rename-a-workspace), the |
| 21 | old remote redirects to the new one for 90 days. Git follows the redirect |
| 22 | and warns about it; point the remote at the new address: |
| 23 | |
| 24 | ```sh |
| 25 | git remote set-url origin https://g1t.sh/<new-workspace>/<repo>.git |
| 26 | ``` |
| 27 | |
| 28 | ## Authentication |
| 29 | |
| 30 | Pushing, and reading private repositories, needs credentials. Use your |
| 31 | username, 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 |
| 33 | individually and they also work for the API. |
| 34 | |
| 35 | To avoid typing it each time, let git store it: |
| 36 | |
| 37 | ```sh |
| 38 | git config --global credential.helper store |
| 39 | ``` |
| 40 | |
| 41 | ## Creating a repository by pushing |
| 42 | |
| 43 | Pushing to a repository that does not exist, in a workspace you belong to, |
| 44 | creates it as a private repository, so nothing pushed by mistake is |
| 45 | published. To make it public, see |
| 46 | [change who can see a repository](/guides/managing-repositories/#change-who-can-see-a-repository). |
| 47 | |
| 48 | ```sh |
| 49 | git push https://g1t.sh/<workspace>/new-repo.git main |
| 50 | ``` |
| 51 | |
| 52 | ## Private repositories |
| 53 | |
| 54 | A private repository is visible only to people with a |
| 55 | [role](/guides/access-and-roles/) on it. Cloning and fetching need |
| 56 | Read, and pushing needs Write. To |
| 57 | everyone else it looks exactly like a repository that does not exist, both |
| 58 | on the site and to git. |
| 59 | |
| 60 | On the site, an address you cannot see gives the same page either way, with |
| 61 | status 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 | |
| 68 | Issues, pull requests and workspace pages work the same way. The sidebar |
| 69 | does not open the project or workspace the address names, so nothing on |
| 70 | the page hints at whether it exists; a missing file, commit or issue in a |
| 71 | project you can see keeps that project's sidebar. A profile that does not |
| 72 | exist says **No one on g1t goes by that name**, since profiles are public. |
| 73 | |
| 74 | ## Browsing without an account |
| 75 | |
| 76 | Public projects, Explore, Search and profiles are open to everyone, in the |
| 77 | same sidebar members use. Signed out, the sidebar has Explore and Search, |
| 78 | and in a project its Code, Issues, Pull requests, Agents, Workflows and |
| 79 | Deployments; pages only people with a role on the repository see, such as |
| 80 | Security and Settings, are left out. **Sign in** and **Sign up** sit at the bottom, and both bring you |
| 81 | back to the page you were on. |
| 82 | |
| 83 | ## Protected branches |
| 84 | |
| 85 | A repository can protect its default branch under **Settings → Branches and |
| 86 | merging**. Pushing to it is then refused for everyone, whatever their role, and for agents, and |
| 87 | git says why: |
| 88 | |
| 89 | ```text |
| 90 | ! [remote rejected] main -> main (main is protected: push a branch and open a pull request) |
| 91 | ``` |
| 92 | |
| 93 | Changes reach a protected branch only by merging a pull request. The first |
| 94 | push to an empty repository is still allowed. |
| 95 | |
| 96 | The same page sets what a merge needs: the |
| 97 | [required status checks](/guides/pull-requests/#required-status-checks) |
| 98 | and approvals. |
| 99 | |
| 100 | ## Branches |
| 101 | |
| 102 | Push any branch to a repository you can write to, and open a |
| 103 | [pull request](/concepts/overview/#pull-requests) from it on the |
| 104 | repository's **Pull requests** tab. |
| 105 | |
| 106 | ```sh |
| 107 | git switch -c my-change |
| 108 | git push origin my-change |
| 109 | ``` |
| 110 | |
| 111 | ## Pull request forks |
| 112 | |
| 113 | A pull request that was not opened from a branch has its own remote: |
| 114 | |
| 115 | ```text |
| 116 | https://g1t.sh/pulls/<pull request id>.git |
| 117 | ``` |
| 118 | |
| 119 | Only whoever opened the pull request can push to it, or, for one g1t |
| 120 | made, whoever asked for it. Pushes to a fork |
| 121 | update the pull request's head commit on its page. |
| 122 | |
| 123 | ## Limits |
| 124 | |
| 125 | ### Size limits |
| 126 | |
| 127 | Repositories are stored in Cloudflare Artifacts. g1t checks its limits |
| 128 | before a push is stored, and declines a push that would cross one. git |
| 129 | prints the reason beside each branch (`! [remote rejected] main (…)`), and |
| 130 | what 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 | |
| 139 | To push a large history in parts: |
| 140 | |
| 141 | ```sh |
| 142 | git rev-list --reverse HEAD | awk 'NR % 500 == 0' | xargs -I{} git push origin {}:refs/heads/main |
| 143 | git push origin main |
| 144 | ``` |
| 145 | |
| 146 | Each push sends only what the one before did not. |
| 147 | |
| 148 | ### When the store is busy |
| 149 | |
| 150 | If Cloudflare Artifacts is rate limiting g1t or not answering, g1t tries |
| 151 | reads again for a moment, then answers git with HTTP `429` (rate limited) |
| 152 | or `503` (unavailable) and a `Retry-After` header saying how many seconds |
| 153 | to wait. Pushes are never tried again on your behalf: run `git push` |
| 154 | again. On g1t.sh the page says the git storage is busy instead of failing, |
| 155 | and [status.g1t.sh](https://status.g1t.sh) shows **Git storage**. |
| 156 | |
| 157 | ### Git operations |
| 158 | |
| 159 | Each clone, fetch and push is a git operation. Every workspace has 50,000 |
| 160 | a month included. Past that, a workspace on the g1t plan pays $0.18 per |
| 161 | 1,000, and a free workspace is never charged: past 50,000 in a month, its |
| 162 | git requests past 60 in an hour are answered `429` with when to try again, |
| 163 | until the month turns. Counting starts on 2026-10-14. See |
| 164 | [git operations](/guides/usage-and-billing/#git-operations). |
| 165 | |
| 166 | What 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 | |
| 171 | Every answer g1t gives git carries a `Server-Timing` header: how many |
| 172 | milliseconds each step of the request took. To see it, run git with its |
| 173 | HTTP trace on: |
| 174 | |
| 175 | ```sh |
| 176 | GIT_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 | |
| 191 | Two 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 | |
| 198 | The ref listing git asks for first on every clone and fetch is kept for up |
| 199 | to a minute, and only the same question about the same refs gets the same |
| 200 | answer: a push, a merge or any other change to a repository's branches and |
| 201 | tags makes the next fetch ask the git store again. A change can take up to |
| 202 | 5 seconds to reach every fetch. |
| 203 | |
| 204 | Include the header when you report a slow clone, fetch or push. |
| 205 | |
| 206 | ## SSH |
| 207 | |
| 208 | Git over SSH is not available yet. Use HTTPS, which works for clone, |
| 209 | fetch and push everywhere SSH would. |
| 210 | |
| 211 | Why: git over SSH needs raw TCP connections on port 22, and g1t runs |
| 212 | entirely on Cloudflare's network. Accepting inbound TCP traffic directly |
| 213 | into Workers is in a beta from Cloudflare that g1t has applied for and is |
| 214 | waiting on. SSH keys can already be added under |
| 215 | [Settings → SSH keys](https://g1t.sh/settings/keys), |
| 216 | and will be used once SSH is on. |