> ## Documentation Index
> Fetch the complete documentation index at: https://code.storage/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Generic Sync

> Mirror a GitLab, Bitbucket, Gitea, Forgejo, Codeberg, or SourceHut repository through Code Storage over HTTPS.

Code Storage can keep a repository in sync with a named Git host that uses HTTPS authentication.
Your tools, automations, and users still use the Code Storage remote.

The `upstream` field of the create-repository request, [`repos.create`](https://code.storage/docs/api/repos/repo-create),
sets the host to sync from. The SDK and the HTTP API use the same field. `base_repo` is a one-time
copy of a Code Storage repository; `upstream` is a lasting link to an external Git host.

Use Generic Sync when you want to:

* Mirror a repository from GitLab, Bitbucket, Gitea, Forgejo, Codeberg, or SourceHut
* Push to Code Storage and let Code Storage forward those writes to the upstream host
* Continue to use Code Storage APIs, JWT-backed remotes, ephemeral branches, and webhooks on the
  mirrored repository

## Sync modes

Code Storage currently supports three Git Sync modes:

| Mode | Best for | Authentication |
| - | - | - |
| [GitHub App](https://code.storage/docs/repos/github-sync.md) | Private GitHub repositories and webhook-driven sync | GitHub App installation token |
| [Public GitHub](https://code.storage/docs/repos/github-sync.md) | Public GitHub repositories you want to pull from without credentials | No GitHub credentials |
| Generic HTTPS Git | GitLab, Bitbucket, Gitea, Forgejo, Codeberg, SourceHut, and similar hosts | Stored username/password or token |

This page covers Generic HTTPS Git. For GitHub, see [GitHub Sync](https://code.storage/docs/repos/github-sync.md).

## Supported providers

Generic Git Sync supports named providers that authenticate over HTTPS with a username and password,
or with a token:

* `gitlab`
* `bitbucket`
* `gitea`
* `forgejo`
* `codeberg`
* `sr.ht` or `sourcehut`

For some providers, Code Storage can derive the public upstream host automatically:

* `gitlab` -> `gitlab.com`
* `bitbucket` -> `bitbucket.org`
* `codeberg` -> `codeberg.org`
* `sr.ht` -> `git.sr.ht`

Set the host (`upstream.host` in the SDK and the HTTP API) for each self-hosted provider. Also set
it for providers with no fixed public host, such as `gitea` and `forgejo`.

Generic HTTPS Git sync does not support [Git LFS](https://code.storage/docs/repos/git-lfs.md).

## Generic Git setup

Generic provider setup happens in three steps:

1. Create the Code Storage repository with a generic Git upstream.
2. Store the HTTPS credential for that repository.
3. Start the initial pull.

### 1. Create the synced repository

This example creates a Code Storage repository with GitLab as its upstream. For a self-hosted GitLab
instance, include `upstream.host`. Set `PIERRE_ORG` and `PIERRE_PRIVATE_KEY` as shown in the
[Quick Start](https://code.storage/docs/getting-started/quick-start.md).

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { Pierre } from '@pierre/sdk';

const pierre = new Pierre();

const repo = await pierre.repos.create({
  repo_name: 'my-gitlab-repo',
  upstream: {
    provider: 'gitlab',
    owner: 'your-group',
    name: 'your-repo',
  },
  default_branch: 'main',
});
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from pierre import Pierre

pierre = Pierre()

repo = pierre.repos.create(
    repo_name="my-gitlab-repo",
    upstream={
        "provider": "gitlab",
        "owner": "your-group",
        "name": "your-repo",
    },
    default_branch="main",
)
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import (
	"context"
	"fmt"

	pierre "github.com/pierrecomputer/sdk/go"
)

ctx := context.Background()
client, err := pierre.NewClient()
if err != nil {
	return err
}

repo, err := client.Repos.Create(ctx, pierre.ReposCreateParams{
	RepoName: pierre.String("my-gitlab-repo"),
	Upstream: pierre.Value(pierre.UpstreamConfig{
		Provider: pierre.UpstreamConfigProviderGitlab,
		Owner:    "your-group",
		Name:     "your-repo",
	}),
	DefaultBranch: pierre.String("main"),
})
if err != nil {
	return err
}
```

```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos" \
  -X POST \
  -H "Authorization: Bearer $PIERRE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "repo_name": "my-gitlab-repo",
    "default_branch": "main",
    "upstream": {
      "provider": "gitlab",
      "owner": "your-group",
      "name": "your-repo"
    }
  }'
```

For a self-hosted provider, add `upstream.host`:

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
const giteaRepo = await pierre.repos.create({
  repo_name: 'my-gitea-repo',
  upstream: {
    provider: 'gitea',
    owner: 'your-org',
    name: 'your-repo',
    host: 'git.example.com',
  },
  default_branch: 'main',
});
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
gitea_repo = pierre.repos.create(
    repo_name="my-gitea-repo",
    upstream={
        "provider": "gitea",
        "owner": "your-org",
        "name": "your-repo",
        "host": "git.example.com",
    },
    default_branch="main",
)
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
giteaRepo, err := client.Repos.Create(ctx, pierre.ReposCreateParams{
	RepoName: pierre.String("my-gitea-repo"),
	Upstream: pierre.Value(pierre.UpstreamConfig{
		Provider: pierre.UpstreamConfigProviderGitea,
		Owner:    "your-org",
		Name:     "your-repo",
		Host:     pierre.String("git.example.com"),
	}),
	DefaultBranch: pierre.String("main"),
})
if err != nil {
	return err
}
fmt.Println(giteaRepo.RepoName)
```

```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos" \
  -X POST \
  -H "Authorization: Bearer $PIERRE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "repo_name": "my-gitea-repo",
    "default_branch": "main",
    "upstream": {
      "provider": "gitea",
      "owner": "your-org",
      "name": "your-repo",
      "host": "git.example.com"
    }
  }'
```

The next steps use the repository name: `repo_name` of the create result in the SDK, or the request
path in the HTTP API. Replace `my-custom-repo` in the HTTP examples with the name of your
repository.

### 2. Store the Git credential

Create a credential record for the repository. Providers that accept a token alone do not need
`username`.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
await pierre.repos.gitCredentials.create({
  repo_name: repo.repo_name,
  username: 'john_doe',
  password: 'YOUR_ACCESS_TOKEN_OR_PASSWORD',
});
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
pierre.repos.git_credentials.create(
    repo_name=repo["repo_name"],
    username="john_doe",
    password="YOUR_ACCESS_TOKEN_OR_PASSWORD",
)
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
_, err = client.Repos.GitCredentials.Create(ctx, pierre.ReposGitCredentialsCreateParams{
	RepoName: repo.RepoName,
	Username: pierre.String("john_doe"),
	Password: "YOUR_ACCESS_TOKEN_OR_PASSWORD",
})
if err != nil {
	return err
}
```

```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos/my-custom-repo/git-credentials" \
  -X POST \
  -H "Authorization: Bearer $PIERRE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john_doe",
    "password": "YOUR_ACCESS_TOKEN_OR_PASSWORD"
  }'
```

### 3. Trigger the initial pull

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
await pierre.repos.upstream.pull({ repo_name: repo.repo_name });
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
pierre.repos.upstream.pull(repo_name=repo["repo_name"])
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
_, err = client.Repos.Upstream.Pull(ctx, pierre.ReposUpstreamPullParams{RepoName: repo.RepoName})
if err != nil {
	return err
}
```

```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos/my-custom-repo/upstream/pull" \
  -X POST \
  -H "Authorization: Bearer $PIERRE_TOKEN" \
  -H "Content-Type: application/json"
```

## How Git Sync behaves

After you configure Git Sync, these rules apply:

* `git clone`, `git fetch`, and `git pull` read from Code Storage
* `repos.upstream.pull` and `POST /api/repos/{repo_name}/upstream/pull` start an asynchronous
  refresh from the configured provider
* Code Storage forwards each `git push` to the external Git host
* A successful push starts a background sync to keep the Code Storage nodes current

Your app can use Code Storage as its stable endpoint. Code Storage still copies changes to and from
the external host.

Only pushes to the normal remote reach the external host. Pushes to the
[`+ephemeral` remote](https://code.storage/docs/repos/ephemeral-branches) stay in Code Storage. A sync from the external host
copies only `refs/heads/*` and `refs/tags/*`. Put machine state on ephemeral branches to keep it out
of the upstream repository. Machine state includes agent snapshots, preview builds, and scratch
commits.

## Related reference pages

* [`repos.create`](https://code.storage/docs/api/repos/repo-create)
* [`repos.gitCredentials.create`](https://code.storage/docs/api/repos/git-credential-create)
* [`repos.upstream.pull`](https://code.storage/docs/api/repos/upstream-pull)
* [GitHub Sync](https://code.storage/docs/repos/github-sync.md)
* [Repository Forks](https://code.storage/docs/repos/forking.md)

## Support

For help, email [jacob@pierre.co](mailto:jacob@pierre.co).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.