# Git Repository Source

> Indexing documentation straight from a git repository — private repos, provider tokens, how syncing and push-triggered re-indexing work.

- Updated: 2026-09-25
- Source: https://contextator.com/en/docs/git-repository-source/
- Language: en-US
- Author: Muhammet Şafak

---
Index documentation straight from a git repository. Contextator keeps a shallow, single-branch checkout
on the server and fetches the branch tip at the start of every index run — or the moment someone
pushes, if you add a webhook.

Works with any HTTPS git server: GitHub, GitLab, Bitbucket, Gitea, Forgejo, Codeberg and self-hosted
instances.

## Adding one

**Add source → Git repository**

| Field | Example | Notes |
|-------|---------|-------|
| **Name** | `api-docs` | The mount prefix. Immutable |
| **Repository URL** | `https://github.com/org/repo.git` | HTTPS only — SSH remotes are not supported |
| **Branch** | `main` | The single branch that is checked out |
| **Subdirectory** | `docs` | Optional. Index only this folder of the repository |
| **Access token** | – | Only for private repositories. Stored encrypted, never shown again |
| **Username** | – | Only needed for a GitLab deploy token (`gitlab+deploy-token-N`) or a Bitbucket Cloud API token (your Bitbucket username or `x-bitbucket-api-token-auth`) |
| **File types** | `.md`, `.mdx` | `.txt`, `.html`/`.htm`, `.csv`, `.docx` and `.pdf` can be selected too |

Press **Test connection** before saving: it lists the remote's refs without cloning and answers with
the branch's current commit, so a wrong URL, branch or token is obvious immediately.

With a subdirectory set, a file at `docs/guides/install.md` in the repository is indexed as
`api-docs/guides/install.md` — the subdirectory itself does not appear in the path.

## Private repositories

Paste an access token into **Access token**. It is encrypted with `SECRET_KEY` (AES-256-GCM) before it
is stored, is never returned by the API and is never shown again — the dialog only says that a token
exists. To replace it, paste a new one; to remove it, tick **Remove the stored token**.

> `SECRET_KEY` must be set (32+ characters, e.g. `openssl rand -hex 32`) before a source can store a
> token. See [Configuration](/en/docs/configuration/).

The username sent with the token depends on the provider and is detected from the URL:

| Provider | Username used | Token types |
|----------|---------------|-------------|
| **GitHub** | `x-access-token` | Classic PAT, fine-grained PAT, App installation token |
| **GitLab** | `oauth2` | OAuth tokens, personal and project access tokens |
| **Bitbucket Cloud** | `x-token-auth` | Repository and workspace access tokens |
| **Bitbucket Cloud (API token)** | *your Bitbucket username*, or `x-bitbucket-api-token-auth` | Put it in the **Username** field — the default `x-token-auth` is for repository/workspace access tokens only |
| **Gitea / Forgejo / Codeberg / other** | `token`, or whatever you type in **Username** | |

Credentials pasted into the URL itself (`https://user:token@host/…`) are stripped before the URL is
stored — use the token field instead.

### Recommended token scopes

Read access to the repository's contents is enough — the narrowest credential each provider offers:

| Provider | Narrowest token | Username |
|----------|------------------|----------|
| **GitHub** | A fine-grained PAT limited to the repository with *Contents: read*, or a GitHub App installation token | leave empty (`x-access-token`) |
| **GitLab** | A project **deploy token** with `read_repository` | the token's generated username, e.g. `gitlab+deploy-token-42` — the default `oauth2` is refused for a deploy token |
| **Bitbucket Cloud** | A repository access token with *Repositories: read* | leave empty (`x-token-auth`) |
| **Gitea / Forgejo** | An access token with read scope on the repository | your username, or leave empty (`token`) |

## Reaching a repository only available over SSH

**Git is read over HTTPS only.** SSH remotes (`ssh://…`, `git@host:path`) are not accepted and no SSH
key can be stored (ADR-0086). For a repository reachable only over SSH, mirror it yourself: clone or
mirror it on the host, inside `ALLOWED_DOC_ROOTS`, keep it current on your own schedule (a cron job
running `git pull`, or your CI), and add that directory as a
[Local Directory Source](/en/docs/local-directory-source/) instead. It is then a folder like any other —
no push webhook, no branch or subdirectory setting, no **Test connection** — and exactly as fresh as your
schedule keeps it.

## How syncing works

At the start of every index run:

1. If there is no checkout yet, clone it: shallow (`depth=1`), single branch, no tags.
2. Otherwise fetch the branch tip. If it moved, point the local branch at it and check out.
3. If anything goes wrong, fall back to a fresh clone.
4. Verify the subdirectory still exists on that branch.

The commit currently checked out is shown on the source row (`main @ a1b2c3d`). Checkouts live under
`DATA_DIR`, so they survive restarts and are deleted with the source.

## Automatic re-indexing on push

Every git source gets its own webhook URL and secret, shown while editing the source:

```
POST http://<your-host>/api/webhooks/git/<source-id>
```

Add it as a **push** webhook in the repository settings with that secret, and every push to the branch
queues a re-index. Full instructions and provider-by-provider screenshots of the fields:
[Push Webhooks](/en/docs/push-webhooks/).

## Common problems

| Symptom | Cause and fix |
|---------|---------------|
| *Authentication failed* on **Test connection** | Token missing, expired, or lacking read access. Leave **Username** empty for a Bitbucket repository or workspace access token (the default `x-token-auth` is for those only); a Bitbucket Cloud API token needs your Bitbucket username or `x-bitbucket-api-token-auth`, and a GitLab deploy token its generated `gitlab+deploy-token-N` username |
| *Branch "…" was not found on the remote* | The branch name is wrong, or the default branch is `master` rather than `main` |
| *Subdirectory "…" does not exist in the repository* | The path is relative to the repository root and is checked against the branch you configured |
| Documents disappeared after a failed sync | They do not — a source that cannot be read keeps its documents. Fix the cause and press **Sync** |
| SSH URL rejected | Only HTTPS is supported. Use the HTTPS URL with a token |

## Notes and limits

- Only one branch per source. To index two branches, add two sources with different names.
- Very large repositories clone more slowly than the native `git` binary would; `depth=1`, a single
  branch and a subdirectory keep it comfortable for documentation.
- Submodules are not fetched.
- The repository is never written to; Contextator only ever fetches.
