> ## 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.

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](/docs/guides/github-sync)    | Private GitHub repositories and webhook-driven sync                       | GitHub App installation token     |
| [Public GitHub](/docs/guides/github-sync) | 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](/docs/guides/github-sync).

## 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 `upstream_host` 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](/docs/guides/git-lfs).

## Generic Git setup

Generic provider setup happens in three steps:

1. Create the Code Storage repository with a generic Git base.
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 base. For a self-hosted GitLab
instance, include `upstreamHost` / `upstream_host`.

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  const repo = await store.createRepo({
    id: 'my-gitlab-repo',
    baseRepo: {
      provider: 'gitlab',
      owner: 'your-group',
      name: 'your-repo',
      defaultBranch: 'main',
    },
    defaultBranch: 'main',
  });
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  repo = await storage.create_repo(
      id="my-gitlab-repo",
      base_repo={
          "provider": "gitlab",
          "owner": "your-group",
          "name": "your-repo",
          "default_branch": "main",
      },
      default_branch="main",
  )
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  repo, err := client.CreateRepo(context.Background(), storage.CreateRepoOptions{
  	ID: "my-gitlab-repo",
  	BaseRepo: storage.GenericGitBaseRepo{
  		Provider:      "gitlab",
  		Owner:         "your-group",
  		Name:          "your-repo",
  		DefaultBranch: "main",
  	},
  	DefaultBranch: "main",
  })
  ```

  ```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  curl "$CODE_STORAGE_BASE_URL/repos" \
    -X POST \
    -H "Authorization: Bearer $CODE_STORAGE_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "default_branch": "main",
      "base_repo": {
        "provider": "gitlab",
        "owner": "your-group",
        "name": "your-repo",
        "default_branch": "main"
      }
    }'
  ```
</CodeGroup>

For a self-hosted provider, add `upstreamHost` / `upstream_host`:

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  const repo = await store.createRepo({
    id: 'my-gitea-repo',
    baseRepo: {
      provider: 'gitea',
      owner: 'your-org',
      name: 'your-repo',
      defaultBranch: 'main',
      upstreamHost: 'git.example.com',
    },
    defaultBranch: 'main',
  });
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  repo = await storage.create_repo(
      id="my-gitea-repo",
      base_repo={
          "provider": "gitea",
          "owner": "your-org",
          "name": "your-repo",
          "default_branch": "main",
          "upstream_host": "git.example.com",
      },
      default_branch="main",
  )
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  repo, err := client.CreateRepo(context.Background(), storage.CreateRepoOptions{
  	ID: "my-gitea-repo",
  	BaseRepo: storage.GenericGitBaseRepo{
  		Provider:      "gitea",
  		Owner:         "your-org",
  		Name:          "your-repo",
  		DefaultBranch: "main",
  		UpstreamHost:  "git.example.com",
  	},
  	DefaultBranch: "main",
  })
  ```

  ```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  curl "$CODE_STORAGE_BASE_URL/repos" \
    -X POST \
    -H "Authorization: Bearer $CODE_STORAGE_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "default_branch": "main",
      "base_repo": {
        "provider": "gitea",
        "owner": "your-org",
        "name": "your-repo",
        "default_branch": "main",
        "upstream_host": "git.example.com"
      }
    }'
  ```
</CodeGroup>

The response includes the internal `repo_id`. Use that value in the next step.

### 2. Store the Git credential

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

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  await store.createGitCredential({
    repoId: repo.id,
    username: 'john_doe',
    password: 'YOUR_ACCESS_TOKEN_OR_PASSWORD',
  });
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  await storage.create_git_credential(
      repo_id=repo.id,
      username="john_doe",
      password="YOUR_ACCESS_TOKEN_OR_PASSWORD",
  )
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  _, err = client.CreateGitCredential(context.Background(), storage.CreateGitCredentialRequest{
  	RepoID:   repo.ID,
  	Username: "john_doe",
  	Password: "YOUR_ACCESS_TOKEN_OR_PASSWORD",
  })
  ```

  ```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  curl "$CODE_STORAGE_BASE_URL/repos/git-credentials" \
    -X POST \
    -H "Authorization: Bearer $CODE_STORAGE_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "repo_id": "REPO_ID_FROM_CREATE_REPO",
      "username": "john_doe",
      "password": "YOUR_ACCESS_TOKEN_OR_PASSWORD"
    }'
  ```
</CodeGroup>

### 3. Trigger the initial pull

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  await repo.pullUpstream();
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  await repo.pull_upstream()
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  err = repo.PullUpstream(context.Background(), storage.PullUpstreamOptions{})
  ```

  ```bash HTTP theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  curl "$CODE_STORAGE_BASE_URL/repos/pull-upstream" \
    -X POST \
    -H "Authorization: Bearer $CODE_STORAGE_TOKEN" \
    -H "Content-Type: application/json"
  ```
</CodeGroup>

## How Git Sync behaves

After you configure Git Sync, these rules apply:

* `git clone`, `git fetch`, and `git pull` read from Code Storage
* `repo.pullUpstream()` and `POST /api/repos/{repo_name}/pull-upstream` 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](/docs/guides/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

* [Create repository](/docs/reference/api/repositories/create-repo)
* [Git credentials](/docs/reference/api/repositories/create-generic-git-credential)
* [Pull from upstream](/docs/reference/api/repositories/pull-upstream)
* [createRepo()](/docs/reference/sdk/create-repo)
* [createGitCredential()](/docs/reference/sdk/create-git-credential)
* [pullUpstream()](/docs/reference/sdk/pull-upstream)
* [GitHub Sync](/docs/guides/github-sync)
* [Repository Forks](/docs/guides/forking)

## Support

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