Skip to content
306 linesCodeBlameRaw
1---
2title: Mirroring
3description: Keep a repository in step with copies of it elsewhere. A mirror stands by as a read-only copy until you take over; hand it back when you're done, or move it to g1t for good.
4---
5
6Mirroring keeps a repository on g1t in step with a copy of it on another
7host: GitHub, another g1t (such as one you run yourself), or any git host
8over HTTPS.
9
10Every linked repository has exactly one **leader**, where work happens. The
11other copies follow it. That one rule answers every question about which
12push wins: a write reaches the leader, or it doesn't land.
13
14| The repository on g1t is | Who leads | What works on g1t |
15| --- | --- | --- |
16| A **mirror**, standing by | The remote | Reading, cloning, search, what security scanning found, insights and settings. Nothing else: it's an exact, read-only copy that runs nothing and opens nothing. |
17| A mirror in **CI failover** | The remote, for code | The same, and the remote's workflows run on g1t. Code, issues and pull requests stay on the remote. |
18| A mirror **taken over** | g1t, for now | Everything: pushes, pull requests, issues, agents, workflows. |
19| **Mirrored to** other hosts | g1t | Everything. Each push is sent to the followers. |
20
21## Make a mirror
22
23- **From GitHub:** choose **New project → Import from GitHub**, pick the
24 repositories and choose **Standby mirror**. See [GitHub](/guides/github/).
25- **From another g1t or any git host:** create an empty repository, then
26 in its **Settings → Mirroring** choose **Add a remote**, with **The remote
27 leads**. Give its https address and a token that can read it. The
28 repository is filled from the remote, and only an empty repository can
29 become a mirror.
30
31A mirror follows every branch and tag of its remote:
32
33- **GitHub** tells g1t about each push, so the mirror catches up within
34 seconds.
35- **Other hosts** are asked every five minutes.
36
37Branches deleted on the remote are deleted on g1t. When the remote
38force-pushes a branch or deletes it, the commit g1t had is kept under
39`refs/g1t/replaced/<branch>/<time>` for at least 30 days. Nothing a mirror
40held is lost silently.
41
42## What a standby mirror offers
43
44A standby mirror is a backup you can switch to: an exact copy of the
45remote, kept up to date, that you can read and clone. It doesn't take work
46of its own, and nothing on g1t opens anything on it by itself. One rule
47decides, by state:
48
49| | Standing by | CI failover | Taken over | Handing back |
50| --- | --- | --- | --- | --- |
51| **Code**: files, commits, branches, tags, compare, releases; clone, fetch, search | ✓ | ✓ | ✓ | ✓ |
52| **Security**: what scanning found, read-only | ✓ | ✓ | ✓ | ✓ |
53| **Insights**, **Settings** (mirroring settings included), rules, webhooks | ✓ | ✓ | ✓ | ✓ |
54| **Workflows**: `.g1t/workflows` | Only with **Keep CI warm** | ✓ | ✓ | – |
55| **Workflows**: `.github/workflows` | – | ✓ | ✓ unless turned off | – |
56| Push, merge, edit on the web, make a release | – | – | ✓ | – |
57| **Issues**, **Pull requests**, **Agents** | – | – | ✓ | – |
58| **Deployments** on g1t.page | – | – | ✓ | – |
59| Security updates, dependency updates, upkeep: anything that opens a branch or a pull request on its own | – | – | ✓ | – |
60
61What it looks like:
62
63- The repository shows **Mirror of github.com/acme/web** beside its name, and
64 so does its row on the workspace's **Projects** page, in the list and the
65 grid, where the **Kind** filter can show **Mirrors** or **On g1t** alone.
66 A mirror in CI failover, taken over or handing back says that instead.
67- Its sidebar lists only what it offers: **Code**, **Security**, **Insights**
68 and **Settings**, plus **Workflows** when they run here. Opening an address
69 it doesn't offer, such as `/acme/web/issues` or `/acme/web/pulls/new`, shows
70 a note that says it's a mirror, links the remote, and leads to **Settings →
71 Mirroring**, instead of the list or the form.
72- Its overview shows the copy: the remote, the default branch, the latest
73 commit, when it last synced, the latest commits and the About card. There
74 is no **Where does it run**, no **Get started** checklist and no
75 **Right now**: nothing runs here.
76- **Security → Vulnerabilities** still lists what the daily scan found, but
77 no security update opens: each alert says the update waits until the
78 repository is taken over or moved to g1t. The same goes for
79 [dependency updates](/guides/dependency-updates/) and anything else g1t
80 does on its own.
81- Every button that would write is greyed out, with the reason on hover, and
82 the services refuse the write as well: a form posted to a mirror answers
83 `409` with the reason, and the API answers `403`.
84
85A `git push` to a standby mirror answers:
86
87```text
88remote: acme/web is a mirror of github.com/acme/web, so it is read-only here.
89remote: Push to github.com/acme/web, or take over in Settings → Mirroring to work on g1t.
90 ! [remote rejected] main -> main
91```
92
93**Taking over** changes all of it at once: the sidebar fills in, the
94overview shows where the project runs and what is moving, security and
95dependency updates open on the next scan, and pushes, pull requests, issues,
96agents, workflows and deployments work until you hand back. **Moving to g1t**
97does the same for good.
98
99## When the remote stops answering
100
101g1t checks each remote's host every minute. A host is **unreachable**
102after three failed checks over at least two minutes. A refused token or a
103deleted repository isn't the host being down; those show as an error on
104the link instead. The host is reachable again after three good checks over
105at least five minutes.
106
107By default an unreachable remote is only **shown**: the repository's dot
108turns amber and a banner offers **Take over**. Nobody is paged and nothing
109happens on its own. If it's midnight and nobody needs to work, GitHub comes
110back and the mirror catches up. Nothing needs reconciling, because nobody
111wrote to g1t.
112
113You choose how much more happens, per link, in **Settings → Mirroring**:
114
115| Setting | Default | Options |
116| --- | --- | --- |
117| When the remote stops answering | Show it on the repository | Also tell the workspace's owners in their [notifications](/guides/notifications/) |
118| Take over automatically | Off | After 5 to 1440 minutes unreachable |
119| Hand back on its own | When every branch goes back cleanly | Only when someone hands it back |
120| Keep CI warm | Off | Run `.g1t/workflows` on each push copied in |
121| Run the remote's `.github` workflows | On | Off |
122| Hold workflows that deploy for approval | On | Off |
123| Report checks to GitHub | On | Off. Only for a mirror of a GitHub repository; see [Checks on GitHub](#checks-on-github). |
124
125**Hand back on its own** only applies to takeovers g1t started itself. A
126takeover a person started waits for a person to hand it back.
127
128## CI failover
129
130Sometimes the remote's git works but its workflows don't run. **Start CI
131failover** keeps the remote in charge of the code and runs its workflows on
132g1t for each push copied in:
133
134- `.github/workflows` as well as `.g1t/workflows`. When both folders have
135 a workflow of the same `name:`, g1t's own runs.
136- Workflows that deploy wait for approval, so nothing deploys from two
137 places. A workflow deploys when any job names an `environment:`.
138- Secrets come from the [project on g1t](/guides/secrets-and-variables/),
139 never the remote's.
140- A push copied in that changes `.g1t/`, `.github/workflows/` or
141 `.github/actions/` starts runs that wait for approval before they can
142 use the repository's secrets and variables.
143
144**End CI failover** when the remote runs its workflows again. Runs already
145started finish.
146
147## Checks on GitHub
148
149While g1t runs a GitHub mirror's workflows, in CI failover, during a
150takeover, or with **Keep CI warm**, it reports their results to the commit
151on GitHub. You can keep GitHub as the place you work and still see, on
152each commit and pull request there, what g1t ran.
153
154What g1t reports is each status it sets on a commit: a workflow run, and a
155deploy. Each becomes a commit status on GitHub:
156
157| On GitHub | What it is |
158| --- | --- |
159| Context | The status's name on g1t under `g1t / `: a workflow run's `CI / push` shows as `g1t / CI / push`. A name that already starts with `g1t / `, such as a deploy's `g1t / deploy`, stays as it is. |
160| State | `pending` while it runs, then `success`, `failure` or `error`, as on g1t. A cancelled run is `error`. |
161| Description | The status's description on g1t, cut to GitHub's 140 characters. |
162| Details | A link to the run, or the deploy, on g1t. |
163
164A status is sent once each time it changes. Only commits that came from
165GitHub are reported: a commit made on g1t during a takeover isn't on
166GitHub until it's handed back, so it is skipped. A mirror standing by
167without **Keep CI warm** runs nothing, so it reports nothing.
168
169g1t reports through its GitHub App, which needs the **Commit statuses**
170permission, read and write. When the app's installation doesn't have it,
171GitHub refuses, and the mirror's **Settings → Mirroring** page says
172**g1t can't report checks to GitHub: the GitHub App needs the Commit
173statuses permission**. g1t doesn't retry; it tries again with the next
174status, and the line goes once a status gets through. To grant it, an
175owner of the GitHub account or organization accepts the app's new
176permissions on GitHub, under **Settings → Applications → Installed GitHub
177Apps → g1t-sh**.
178
179To turn it off, clear **Report checks to GitHub** in the mirror's
180**Settings → Mirroring** and save. Through the API, send
181`report_checks: false` to
182`PATCH /repos/{owner}/{name}/mirror/remotes/{id}`.
183
184## Take over
185
186**Take over** makes g1t lead the repository for now, whether or not the
187remote is answering. Everything works on g1t from then on. Each branch's
188commit at that moment is recorded as where the takeover began.
189
190You can take over any time: during an outage g1t detected, a partial one
191it didn't, or simply because you want to work on g1t for a while.
192
193If the remote is still answering, people may keep pushing there. Their
194work isn't lost. Each branch that changed on both sides waits for a
195decision when you hand back.
196
197## Hand back
198
199**Review hand-back** shows what handing back would do, branch by branch,
200by comparing each side with where the takeover began:
201
202| Plan | When | What happens |
203| --- | --- | --- |
204| **Push** | Only g1t changed it | g1t's commits are pushed to the remote. |
205| **Take** | Only the remote changed it | The remote's commits are copied in. |
206| **Pull request** | Only g1t changed it, and the remote protects the branch | g1t's commits go to `g1t/handback/<branch>` on the remote, with a pull request into the branch. g1t then follows the remote's branch. |
207| **Diverged** | Both changed it | You decide: keep g1t's (pushed over the remote's), keep the remote's (g1t's is kept under `refs/g1t/replaced/`), or send g1t's as a pull request. |
208
209**Hand back** carries out the plan. The repository is read-only while it
210goes back, so nothing moves under it. If the remote refuses anything, g1t
211keeps the lead and says why; nobody is left stuck. When every branch has
212gone back, the mirror stands by again and lists any pull requests it
213opened.
214
215Hand back is refused while the remote isn't answering, and while a
216diverged branch has no decision.
217
218## Move to g1t
219
220**Move to g1t** makes the move permanent. The repository stops being a
221mirror, and **g1t no longer tracks the remote**: pushes made there don't
222come here any more. You can move from standby, CI failover or during a
223takeover, and the takeover's work stays as it is.
224
225Tick **Keep the remote updated from g1t** to turn the remote into a
226follower instead of dropping it. g1t then pushes every change to it, as
227below.
228
229## Mirror to other hosts
230
231A repository g1t leads can be **mirrored to** any number of followers.
232Use another g1t to keep a copy on your own servers, or keep GitHub up to
233date for people still working there.
234
235In **Settings → Mirroring**, **Add a remote** with **g1t leads**, its https
236address, a username if the host needs one, and a token that can push. The
237token is stored encrypted and never shown again. Repositories imported
238from GitHub with **Move to g1t** follow g1t this way already.
239
240Each push to g1t is sent to every follower. When someone pushes to a
241follower directly:
242
243- **Take in fast-forwards** (the default): a push that builds on g1t's
244 branch is copied in. Anything else marks the follower **stuck**. g1t
245 stops pushing to it until someone chooses **Sync now**, which pushes
246 g1t's branches over it.
247- **Overwrite with g1t's**: g1t pushes its branches over the change at once.
248
249A follower that refuses g1t's push, a protected branch say, is also marked
250stuck, with its answer on the link.
251
252## Self-hosted g1t and g1t.sh
253
254The `g1t` remote works both ways between instances:
255
256- Run g1t on your own servers as the leader and mirror to
257 [g1t.sh](https://g1t.sh) for a hosted copy, or the other way round.
258- Make a standby mirror on your own g1t of a repository on g1t.sh. Take
259 over if g1t.sh is unreachable from your network.
260
261Use a [token](/guides/authentication/) with `code:read` for a leader, or
262`code:write` for a follower, on the other instance.
263
264## API, MCP and events
265
266Everything here is also in the API at `/repos/{owner}/{name}/mirror`, and
267as the `mirror_*` actions of the MCP `repository` tool. See the
268[API reference](/reference/api/). Reading a repository's
269mirroring needs read access; syncing needs push; everything else needs the
270Admin role. Agents never move a repository to g1t, nor add, change or
271remove a remote.
272
273Removing a remote makes a mirror an ordinary repository with what it has,
274and stops pushes to a follower. It is refused during a takeover: hand it
275back or move it to g1t first.
276
277[Webhooks](/guides/webhooks/) can subscribe to:
278
279- `mirror.unreachable` and `mirror.reachable`
280- `mirror.state_changed`, sent when a mirror stands by, enters CI
281 failover, is taken over or is handed back
282- `mirror.moved_in`, sent when a mirror is moved to g1t or stops because
283 its remote is gone
284
285## When a remote goes away
286
287A standby mirror becomes an ordinary repository, keeping what it has, when:
288
289- the repository is deleted on GitHub,
290- g1t's GitHub App is uninstalled from its account, or
291- a workspace owner removes that GitHub account from the workspace.
292
293A takeover in progress keeps going and says why on the link: move it to
294g1t to keep it.
295
296## Not supported yet
297
298- Checks go back to GitHub as commit statuses, one per workflow run; a
299 run's jobs aren't shown there as separate check runs. Remotes other than
300 GitHub aren't told about checks.
301- CI failover starts only when someone starts it.
302- A takeover runs under the repository's own [rules](/guides/rules/) on
303 g1t, not the remote's branch protection.
304- A mirror doesn't show the remote's pull requests.
305- GitLab and Bitbucket repositories link as any host over HTTPS, asked
306 every five minutes.