Skip to content

Commit 62b1993

Browse files
committed
docs: pull request head mirroring
1 parent 861e915 commit 62b1993

3 files changed

Lines changed: 57 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- `git-sync` can mirror the commits behind open pull requests. A pull request opened from a fork has no branch in the source repository, so its commits previously reached no destination at all; with `pull_requests.enabled` on a mirror, the head is pushed as an ordinary branch under `branch_prefix` (default `syncerd/pr/<number>`), and the default mirror push mode prunes it once the pull request closes. A head that lives in the source repository already arrives with the ordinary branch mirror and gets no second copy. A repository whose own branches sit under the prefix is refused rather than silently overwritten, and one unreachable fork head is a warning rather than a failed repository. Off by default: a fork head is third party code, and a destination that builds on branch push would run it. GitHub sources only for now; the pull request objects themselves are not recreated at the destination yet
12+
1013
### Fixed
1114
- `git-sync` discovered only the public repositories of a GitHub personal account. The listing fell back to `GET /users/{owner}/repos` when the owner was not an organisation, and that endpoint returns public repositories only, whatever token is presented, so private repositories were silently missing from every run with no error to point at. When the token belongs to the configured owner, SyncerD now lists through `GET /user/repos?visibility=all&affiliation=owner`, which reports private repositories. Mirroring another account still sees its public repositories only, since no credential can see further
1215
- `git-sync` failed outright at the `discover` stage when a GitLab source was a user namespace rather than a group: only `GET /groups/{owner}/projects` was tried, and it 404s for a personal namespace. A 404 on the first page now falls back to `GET /users/{owner}/projects`, which lists private projects the token can see

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ Override with `SYNCERD_` prefix:
314314

315315
## Git mirroring
316316

317-
`syncerd git-sync` mirrors git repositories between hosting providers, replicating branches and tags as a full mirror in either direction. All five provider types are supported: GitHub, GitLab, Bitbucket, Azure DevOps, and AWS CodeCommit, and any of them can be a source or a destination.
317+
`syncerd git-sync` mirrors git repositories between hosting providers, replicating branches, tags, and optionally the heads of open pull requests as a full mirror in either direction. All five provider types are supported: GitHub, GitLab, Bitbucket, Azure DevOps, and AWS CodeCommit, and any of them can be a source or a destination.
318318

319319
**Providers:**
320320

@@ -378,6 +378,8 @@ export SYNCERD_GIT_GL_TOKEN=your-gitlab-token
378378

379379
**Adopt guard:** a mirror refuses to push to a destination that already has content and no prior mirror state, so a misconfigured mirror cannot silently overwrite existing work; set `adopt: true` on the mirror to opt in once you've confirmed the destination is right.
380380

381+
**Pull request heads (opt in):** set `pull_requests.enabled: true` on a mirror and the commits behind each open pull request opened from a **fork** are pushed to the destination as a branch, default `syncerd/pr/<number>`. Without it those commits reach the destination nowhere, since a fork pull request has no branch in the source repository. A pull request opened from a source branch already mirrors under its own name and gets no second copy. The pull request itself is not recreated at the destination yet. Off by default because a fork head is third-party code, and a destination that builds on branch push would run it: read the security note in [docs/git-sync-runbook.md](docs/git-sync-runbook.md#mirroring-pull-request-heads) before enabling it.
382+
381383
Run with `--dry-run` to print the ref changes each mirror would make, per repository, without creating, pushing, or deleting anything.
382384

383385
**Limitations:**

docs/git-sync-runbook.md

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,9 +591,57 @@ falls back to `GET /users/{owner}/projects`.
591591
no bundled git; install git 2.30 or newer on any host that runs a release
592592
binary. The Docker image installs it for you.
593593

594-
**Only branches and tags are mirrored.** Not issues, pull requests, wikis,
595-
releases, or Git LFS objects. A repository using LFS mirrors its pointers, not
596-
its objects.
594+
**Branches, tags, and optionally pull request heads are mirrored.** Not issues,
595+
pull request discussions, wikis, releases, or Git LFS objects. A repository
596+
using LFS mirrors its pointers, not its objects. With `pull_requests.enabled`
597+
set on a mirror, the commits behind each open pull request opened from a fork
598+
are pushed as a branch; see Mirroring pull request heads above.
599+
600+
---
601+
602+
## Mirroring pull request heads
603+
604+
```yaml
605+
git:
606+
mirrors:
607+
- name: gh-to-gl
608+
source: gh
609+
destination: gl
610+
pull_requests:
611+
enabled: true # default false
612+
branch_prefix: syncerd/pr # default
613+
states: [open] # default, and the only accepted value today
614+
```
615+
616+
A pull request opened from a branch of the source repository needs nothing: its
617+
commits already arrive with the ordinary branch mirror, under their own branch
618+
name. A pull request opened from a **fork** has no branch in the source
619+
repository, so its commits reach the destination only with this setting on, as
620+
`refs/heads/<branch_prefix>/<number>`.
621+
622+
Under the default `mirror` push mode the branch disappears from the destination
623+
on the first run after the pull request closes: the source stops listing it, and
624+
prune removes what the source no longer has.
625+
626+
The pull request object itself, its title, discussion, and reviews, is not
627+
recreated at the destination. Only the commits are.
628+
629+
**Read this before enabling it.** A fork pull request head is code written by
630+
anyone who can open a pull request against the source. Turning this on pushes
631+
that code to a destination branch, where it arrives as an ordinary push from a
632+
trusted mirror rather than as a fork pull request. If destination CI builds on
633+
branch push, it will run that code with whatever credentials that CI holds, and
634+
none of the source's fork pull request protections apply. Restrict destination
635+
CI to the branches your team actually builds and exclude the `branch_prefix`
636+
namespace. It is one contiguous namespace so a single glob covers it.
637+
638+
A repository whose own branches live under `branch_prefix` is refused with an
639+
error naming the branch, because mirroring both would have the two overwrite
640+
each other on every run. Change `branch_prefix` for that mirror, or exclude the
641+
repository.
642+
643+
Only GitHub sources can list pull requests today. A mirror that enables
644+
`pull_requests` with any other source type is rejected at startup.
597645

598646
---
599647

0 commit comments

Comments
 (0)