| 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 | ## Download a ZIP |
| 75 | |
| 76 | On a repository's **Files** page, **Code** → **Download ZIP** downloads the |
| 77 | branch shown as one zip, its files in a folder named `<repo>-<branch>`. It |
| 78 | works for anyone who can see the repository, and for any branch, tag or |
| 79 | commit at `g1t.sh/<workspace>/<repo>/archive/<ref>.zip`. A ZIP holds the |
| 80 | files, not the history; clone for that. A repository with more than 10,000 |
| 81 | files or over 24 MB is too large to download this way, so clone it instead. |
| 82 | |
| 83 | ## Browsing without an account |
| 84 | |
| 85 | Public projects, Explore, Search and profiles are open to everyone, in the |
| 86 | same sidebar members use. Signed out, the sidebar has Explore and Search, |
| 87 | and in a project its Code, Issues, Pull requests, Agents, Workflows and |
| 88 | Deployments; pages only people with a role on the repository see, such as |
| 89 | Security and Settings, are left out. **Sign in** and **Sign up** sit at the bottom, and both bring you |
| 90 | back to the page you were on. |
| 91 | |
| 92 | ## Protected branches |
| 93 | |
| 94 | A repository can protect its default branch under **Settings → Branches and |
| 95 | merging**. Pushing to it is then refused for everyone, whatever their role, and for agents, and |
| 96 | git says why: |
| 97 | |
| 98 | ```text |
| 99 | ! [remote rejected] main -> main (main is protected: push a branch and open a pull request) |
| 100 | ``` |
| 101 | |
| 102 | Changes reach a protected branch only by merging a pull request. The first |
| 103 | push to an empty repository is still allowed. |
| 104 | |
| 105 | The same page sets what a merge needs: the |
| 106 | [required status checks](/guides/pull-requests/#required-status-checks) |
| 107 | and approvals. |
| 108 | |
| 109 | ## Branches |
| 110 | |
| 111 | Push any branch to a repository you can write to, and open a |
| 112 | [pull request](/concepts/overview/#pull-requests) from it on the |
| 113 | repository's **Pull requests** tab. |
| 114 | |
| 115 | ```sh |
| 116 | git switch -c my-change |
| 117 | git push origin my-change |
| 118 | ``` |
| 119 | |
| 120 | A repository's **Branches** tab, `g1t.sh/<workspace>/<repo>/branches`, lists |
| 121 | every branch: the default one first, then those with a commit in the last 90 |
| 122 | days (**Active**), then the rest (**Stale**). Each shows its last commit, how |
| 123 | many commits it is ahead of and behind the default branch, the pull request |
| 124 | open on it with its checks, and its preview when it has one. A count with a |
| 125 | `+` ran past how far back g1t reads, 40 commits on the branch and 120 on the |
| 126 | default. Search narrows the list by name. |
| 127 | |
| 128 | The **Tags** tab lists tags newest first, up to 100, each with its commit |
| 129 | and a ZIP of its files. |
| 130 | |
| 131 | **Compare**, `g1t.sh/<workspace>/<repo>/compare/<base>...<head>`, shows |
| 132 | what one branch has that another does not: its commits, then every change. |
| 133 | Pick the two branches at the top; **Open a pull request** starts one from |
| 134 | the compared branch. |
| 135 | |
| 136 | On **Files**, each file and folder shows the commit that last changed it and |
| 137 | when, from up to 300 commits of the branch's history; one changed before |
| 138 | that shows none. The branch menu at the top switches branch and keeps the |
| 139 | folder or file you are on. |
| 140 | |
| 141 | ## Pull request forks |
| 142 | |
| 143 | A pull request that was not opened from a branch has its own remote: |
| 144 | |
| 145 | ```text |
| 146 | https://g1t.sh/pulls/<pull request id>.git |
| 147 | ``` |
| 148 | |
| 149 | Only whoever opened the pull request can push to it, or, for one g1t |
| 150 | made, whoever asked for it. Pushes to a fork |
| 151 | update the pull request's head commit on its page. |
| 152 | |
| 153 | ## Limits |
| 154 | |
| 155 | ### Size limits |
| 156 | |
| 157 | Repositories are stored in Cloudflare Artifacts. g1t checks its limits |
| 158 | before a push is stored, and declines a push that would cross one. git |
| 159 | prints the reason beside each branch (`! [remote rejected] main (…)`), and |
| 160 | what to do as `remote:` lines. Nothing in a declined push is stored. |
| 161 | |
| 162 | | Limit | Size | What happens past it | |
| 163 | | --- | --- | --- | |
| 164 | | A file | 32 MB | The push is declined, naming the file's size. | |
| 165 | | 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. | |
| 166 | | 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. | |
| 167 | | A push | 100 MB | Refused by the network with HTTP `413` before g1t sees it. | |
| 168 | |
| 169 | To push a large history in parts: |
| 170 | |
| 171 | ```sh |
| 172 | git rev-list --reverse HEAD | awk 'NR % 500 == 0' | xargs -I{} git push origin {}:refs/heads/main |
| 173 | git push origin main |
| 174 | ``` |
| 175 | |
| 176 | Each push sends only what the one before did not. |
| 177 | |
| 178 | ### When the store is busy |
| 179 | |
| 180 | If Cloudflare Artifacts is rate limiting g1t or not answering, g1t tries |
| 181 | reads again for a moment, then answers git with HTTP `429` (rate limited) |
| 182 | or `503` (unavailable) and a `Retry-After` header saying how many seconds |
| 183 | to wait. Pushes are never tried again on your behalf: run `git push` |
| 184 | again. On g1t.sh the page says the git storage is busy instead of failing, |
| 185 | and [status.g1t.sh](https://status.g1t.sh) shows **Git storage**. |
| 186 | |
| 187 | ### Git operations |
| 188 | |
| 189 | Each clone, fetch and push is a git operation, your agents' included: |
| 190 | their sandboxes use the same git endpoints you do, and a pull request's |
| 191 | working copy counts for its repository's workspace. Every workspace has 50,000 |
| 192 | a month included. Past that, a workspace on the g1t plan pays $0.18 per |
| 193 | 1,000, and a free workspace is never charged: past 50,000 in a month, its |
| 194 | git requests past 60 in an hour are answered `429` with when to try again, |
| 195 | until the month turns. Counting starts on 2026-10-14. See |
| 196 | [git operations](/guides/usage-and-billing/#git-operations). |
| 197 | |
| 198 | What these limits mean in practice, and what to do instead, is on |
| 199 | [What g1t can't do yet](/about/limitations/#git). |
| 200 | |
| 201 | ## Where a slow request's time went |
| 202 | |
| 203 | Every answer g1t gives git carries a `Server-Timing` header: how many |
| 204 | milliseconds each step of the request took. To see it, run git with its |
| 205 | HTTP trace on: |
| 206 | |
| 207 | ```sh |
| 208 | GIT_TRACE_CURL=1 git ls-remote https://g1t.sh/<owner>/<repo>.git 2>&1 | grep -i server-timing |
| 209 | ``` |
| 210 | |
| 211 | | Step | What it is | |
| 212 | | --- | --- | |
| 213 | | `repo` | Finding the repository, and checking your credentials if you sent any | |
| 214 | | `moved` | Only for an address with no repository: looking for a renamed workspace or a transferred repository to send you to | |
| 215 | | `access` | Deciding whether you may fetch from or push to it | |
| 216 | | `kept` | A free workspace's limits, and looking for a ref listing and a store credential made a moment ago | |
| 217 | | `mint` | Only when no credential was kept: the git store making one for the request | |
| 218 | | `store` | The git store's answer; for a push, checking it for secrets first | |
| 219 | | `refs` | Only for a push: recording that the repository's refs changed | |
| 220 | | `total` | Everything g1t did | |
| 221 | | `repos` | The same, measured where your request arrived | |
| 222 | |
| 223 | Two entries say how a step went rather than how long it took: |
| 224 | |
| 225 | | Entry | Values | |
| 226 | | --- | --- | |
| 227 | | `refs;desc=` | `hit-colo` or `hit-shared` when the ref listing came from g1t's cache, `miss` when the git store was asked | |
| 228 | | `pack;desc=` | Only for a fresh clone: `hit` when its pack came from g1t's cache, `miss` when the git store built it | |
| 229 | | `cred;desc=` | `isolate` or `shared` for a store credential made a moment ago, `mint` for a new one | |
| 230 | |
| 231 | The ref listing git asks for first on every clone and fetch is kept for up |
| 232 | to a minute, and only the same question about the same refs gets the same |
| 233 | answer: a push, a merge or any other change to a repository's branches and |
| 234 | tags makes the next fetch ask the git store again. A change can take up to |
| 235 | 5 seconds to reach every fetch. |
| 236 | |
| 237 | A fresh clone, one that has no objects yet (shallow clones such as |
| 238 | `git clone --depth=1` included), has its pack kept too, for up to 7 days |
| 239 | or until the repository's branches or tags next change. The next clone that |
| 240 | asks for the same commits in the same way gets the same pack without the |
| 241 | git store building it again. A fetch into a repository you already have, |
| 242 | and any pack over 200 MB, always goes to the git store. |
| 243 | |
| 244 | Include the header when you report a slow clone, fetch or push. |
| 245 | |
| 246 | ## SSH |
| 247 | |
| 248 | Git over SSH is not available yet. Use HTTPS, which works for clone, |
| 249 | fetch and push everywhere SSH would. |
| 250 | |
| 251 | Why: git over SSH needs raw TCP connections on port 22, and g1t runs |
| 252 | entirely on Cloudflare's network. Accepting inbound TCP traffic directly |
| 253 | into Workers is in a beta from Cloudflare that g1t has applied for and is |
| 254 | waiting on. SSH keys can already be added under |
| 255 | [Settings → SSH keys](https://g1t.sh/settings/keys), |
| 256 | and will be used once SSH is on. |