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

# Git Sync

> Version-control supported Hightouch resources in a Git repository.

> [!NOTE]
>
> Git Sync is only available on [Business tier plans](https://hightouch.com/pricing).

<video controls src="https://cdn.sanity.io/files/pwmfmi47/production/28b600cb8a0e813b85206e5fbd2f44db9ebac380.mp4" poster="https://cdn.sanity.io/images/pwmfmi47/production/268d6fd4a5a5723ed2f0815a1ecdc63988180f71-3024x1700.webp" title="Git Sync and dbt Extensions Overview" />

Git Sync connects your Hightouch workspace to a Git repository so you can version-control supported Hightouch resources.

With Git Sync, you can:

* Create and edit multiple Hightouch models and syncs in code
* Review changes through Git commits and file diffs
* Roll resources back to a previous version
* Keep staging and production workspaces aligned through shared YAML files

Git Sync is bidirectional:

* Changes made in the Hightouch UI are written to YAML files in Git. These changes are marked as `OUTBOUND`.
* Changes made to YAML files in Git are applied back to Hightouch. These changes are marked as `INBOUND`.

Each update to a supported resource creates a commit in your configured repository.

Git Sync currently supports models (including Customer Studio schema models), syncs, and event contracts (domains, event schemas, and components). Git Sync does not currently support Customer Studio audiences or syncs created from those audiences.

![Git Sync diagram](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=80acfefe5836f786d0e0ad5641f48317)

> [!NOTE]
>
> Inbound changes made in your Git Sync repository don't require approval from
> another team member, even if your workspace uses [approval
> flows](https://docs.hightouch.com/workspace-management/approval-flows.md).

Git Sync supports most Git providers, including GitHub, Bitbucket, GitLab, and private [on-premise](#tunneling) repositories. In most cases, changes sync between Hightouch and Git in about one minute.

> [!NOTE]
>
> Hightouch can connect to Git repositories for two different purposes. Git
> Sync uses a Git repository to version-control Hightouch resources, which is
> separate from Git repositories used to [leverage dbt
> models](https://docs.hightouch.com/extensions/dbt-models.md).

## Setup

### 1. Create or choose a Git repository

Use a Git repository to store the YAML files that represent your supported Hightouch resources.

If you are creating a new repository, GitHub repositories are the easiest to start with because they can use the Hightouch GitHub app.

> [!TIP]
>
> If you create an empty repository, make an initial commit, such as adding a
> README file. This creates the branch Hightouch needs to connect to.

### 2. Authenticate to Git

After you have a repository, configure your Git credentials in Hightouch.

1. Go to the [Git Sync configuration page](https://app.hightouch.com/extensions/git-sync/configuration?from=docs).
2. Under **Configuration > Git credentials**, select **Set up**.
3. Choose your Git provider and authentication method.

If you use GitHub, we recommend using the [Hightouch GitHub app](https://github.com/apps/hightouch-connect/) for the most up-to-date feature support.

![Selecting the GitHub app](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-github-app.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=c486bf7a97255cfc0e3d5dfbba3e277a)

Regardless of provider, grant Hightouch access only to the repository or repositories you want to use with Git Sync.

#### Authenticate to a GitHub Enterprise repository

If you use GitHub Enterprise with a self-hosted server:

1. Under **Git service**, select **Other**.
2. For **Username**, enter your full GitHub Enterprise username. For example, `hightouch123@github.hightouch.com`.
3. For **Token**, enter a [GitHub access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) tied to that username.
4. Make sure the token has access to the repository or repositories Hightouch should use.

   ![Configuring GitHub Enterprise](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-github-enterprise.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=9bcdb05dc1a4d7a0dfb203bdf2c65f7e)

#### Authenticate to an AWS CodeCommit repository

If you use AWS CodeCommit, you must use SSH to authenticate.

1. Configure your AWS CodeCommit repository for SSH by following [AWS's setup instructions](https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-ssh-unixes.html).

2. In Hightouch, select **Other** as the **Git service**.

3. Select **SSH** as the **Protocol**.

   ![Configuring AWS CodeCommit](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-ssh-auth.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=63790f3d52f8eae52b62f9e3c248535d)

4. Under **SSH Private key**, enter the private key you generated when configuring AWS CodeCommit.

5. Select **Save**.

6. For **Repository**, enter the full SSH URL, including the AWS SSH key ID.

The AWS SSH key ID is different from the general access keys in your AWS account.

![Configuring AWS CodeCommit](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-aws-ssh-id-1.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=6c3917a140258878e4dbf069c4b2a7cc)

![Configuring AWS CodeCommit](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-aws-ssh-id-2.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=69c39dc0ed1dee7587aa7a31fec2ee42)

You can copy your URL from these [instructions in AWS's docs](https://docs.aws.amazon.com/codecommit/latest/userguide/how-to-connect.html).

![Configuring AWS CodeCommit](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-aws-docs.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=9a67ba9a0e71528a72a58bcb9c5e79ce)

### 3. Configure Git Sync

After you authenticate to Git, enable Git Sync for your workspace.

1. Go to [Integrations > Extensions](https://app.hightouch.com/extensions/git-sync/configuration?from=docs).

2. Open the **Version control with Git** extension.

3. On the **Configuration** tab, enable **Git Sync**.

   ![Enabling Git Sync on the Configuration tab](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/extensions/git-sync/git-sync-enabled-toggle.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=3459005d27fb09ef830cc838e9a9c1fd)

4. Select the **Repository** and **Branch** Hightouch should read from and write to.

5. Optionally, enter a **Path** where Hightouch should create folders for Git Sync files.

6. Select **Save**.

If you enter a path, Hightouch creates the `models`, `syncs`, and other supported folders inside that subfolder. If you don't enter a path, Hightouch saves Git Sync files in the root of your repository.

![Models and syncs folders in a Git repository](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-repo-synced.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=5ffa066ce13cd8dcb0f8f9752e13f0dd)

After you save the configuration, Hightouch creates the configured folder structure if needed. Git Sync then starts syncing supported resources between Hightouch and Git.

> [!NOTE]
>
> To start the syncing process immediately, select **Full resync**.
>
> This pushes all supported workspace resources to Git, then pulls changes from Git back to your workspace. Because this happens in that order, a full resync can overwrite Git changes that have not synced to Hightouch yet.
>
> For more information, see [What does the Full resync button do?](#what-does-the-full-resync-button-do).

> [!WARNING]
>
> Git Sync does not support deletions from Git.
>
> If you delete a YAML file in Git, the corresponding model or sync remains in Hightouch. The file is not recreated in Git until the next change is made in Hightouch or you trigger a [full resync](#what-does-the-full-resync-button-do).

## Use Git Sync with branch protection

If your repository uses branch protection, you may need to let the Hightouch Connect app bypass specific branch rules. The Hightouch Connect app can only push to a protected branch if you add it to the bypass list under GitHub rulesets.

To configure this in GitHub:

1. Go to **Settings > Rules > Rulesets**.
2. Add the Hightouch Connect app to the **Bypass list**.
3. Configure the rest of the ruleset to enforce your branch protection requirements.

> [!NOTE]
>
> GitHub supports branch protection in both **Settings > Branches** and **Settings > Rules > Rulesets**.
>
> The Hightouch Connect app only works with branch protection when you add it to the **Bypass list** under **Rulesets** and define the rest of your branch protection rules there. It does not work as a force-push app under **Branches**.

## Connect staging and production workspaces

You can use Git Sync to keep staging and production workspaces aligned through the same Git repository. When multiple workspaces connect to the same repository, [alias files](#aliases) handle environment-specific source and destination IDs.

![Multiple workspaces in the same organization can connect to the same Git repository](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/workspace-management/permissions/select-a-workspace.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=42c6062325f2652ae1440994d76f8ccf)

### Use the same branch

For most teams, the simplest setup is to point both workspaces to the same repository and branch, such as `main`.

1. In each workspace, go to the [Git Sync configuration page](https://app.hightouch.com/extensions/git-sync/configuration?from=docs).
2. Select the same repository and branch.
3. Share model and sync YAML files between both workspaces.
4. Create [alias files](#aliases) for each workspace to map shared aliases to the correct source and destination IDs.

For example, both workspaces can reference `aliased-postgres` in their model files. The staging alias file maps `aliased-postgres` to `staging-postgres`, while the production alias file maps it to `production-postgres`.

> [!TIP]
>
> For a full staging-to-production example, see [Aliases](#aliases).

### Use separate branches

For a more controlled promotion workflow, use a separate branch for each environment.

1. Point your staging workspace to a development branch, such as `develop`.
2. Point your production workspace to your production branch, such as `main`.
3. Test and iterate in staging. Changes sync to the development branch.
4. When ready, open a pull request to merge the development branch into the production branch.
5. After the pull request is merged, Git Sync picks up the changes in your production workspace.

Use [CI checks](#validate-changes) to validate changes before they reach production.

### Understand what syncs across environments

Git Sync can version-control and replicate the following resources across environments:

* Models
* Syncs
* Event contracts (domains, event schemas, and components)
* Customer Studio schema models, including parent, related, and event models
* Customer Studio schema relationships

Git Sync does not replicate everything. Configure the following manually in each workspace:

* Customer Studio traits, audiences, and syncs from audiences
* Workspace-level settings, such as approval flows, spaces, and environments
* Column-level schema settings configured in the UI, such as redaction, column suggestions, and display configuration

For Customer Studio schema setup instructions, see [Configure Schema Git Sync](https://docs.hightouch.com/customer-studio/schema-setup.md).

## Schemas

Git Sync represents supported Hightouch resources as YAML files in your Git repository.

> [!WARNING]
>
> Field names in YAML files are case-sensitive. For example, a field named
> `Email` is not the same as `email`. This applies to all field references,
> including source column names, primary keys, and field mappings. If you rename
> a column in your warehouse changing only the casing, Git Sync treats it as a
> different field, which can cause Git Sync errors or unexpected behavior.

> [!WARNING]
>
> Schemas created by Identity Graphs, like the Golden
> Record, will
> **not** be displayed in your ./models folder. This is because the model is
> fully managed by Hightouch rather than user-authored.

### Model schema

Models use the following schema:

```yaml theme={null}
name: > the model's display name. the model's slug is the YAML filename
source: > the name/slug of the source. the slug is visible on the source page
type: > one of: raw_sql, table, or dbt_model
dbtModel: > null or the name of the dbt model to select, written as `model.<package_name>.<model_name>`
rawSql: > null or the sql to use, for example, select * from albums
tableName: > null or the table name to use
isSchema: > used internally by Hightouch, this should always be false
primaryKey: > primary key column for this model, for example, album_id
```

#### Model field reference

| Field | Required | Type | Valid values | Notes |
| - | - | - | - | - |
| `name` | Yes | string | Any string | Display name. Doesn't need to match the YAML filename. |
| `source` | Yes | string | Source slug | Find the slug on the source's settings page in Hightouch. |
| `type` | Yes | string | `raw_sql`, `table`, `dbt_model` | Determines which of the next three fields is used. |
| `rawSql` | Conditional | string or null | Valid SQL | Required when `type: raw_sql`. Set to `null` otherwise. |
| `tableName` | Conditional | string or null | Table reference | Required when `type: table`. Set to `null` otherwise. |
| `dbtModel` | Conditional | string or null | `model.<package>.<name>` | Required when `type: dbt_model`. Set to `null` otherwise. |
| `isSchema` | Yes | boolean | `false` | Always set to `false` for standard models. |
| `primaryKey` | Yes | string | Column name | Must match an existing column in the model's query results. |

> [!NOTE]
>
> A model's slug is its YAML filename. For example, `ab-01-a.yaml` has the slug
> `ab-01-a`. Syncs and relationships refer to models by slug, not by `name`.
>
> To update an existing model, keep the same filename. If you rename the file,
> Git Sync creates a new model instead of updating the existing one. You can
> change `name` without affecting the slug.

### Sync schemas

Sync schemas vary by destination. Because each destination has different configuration options, we recommend creating syncs in the Hightouch UI first. You can then use the generated YAML file as a starting point for updates or new syncs. The YAML filename is the sync name.

```yaml theme={null}
model: > slug of the model for this sync (its YAML filename, without .yaml)
destination: > destination name/slug for this sync
config:
    > destination dependent configuration for this sync.
schedule: > type of schedule to use. the format here varies based on the
            schedule selected, we recommend using the UI as a starting point.
schedulePaused: > boolean, where this sync is paused, or enabled.
```

#### Sync field reference

| Field | Required | Type | Valid values | Notes |
| - | - | - | - | - |
| `model` | Yes | string | Model slug | Must match the filename of an existing model YAML file, without `.yaml`. |
| `destination` | Yes | string | Destination slug | Find the slug on the destination's settings page or in the [manifest file](#manifests). |
| `config` | Yes | object | Destination-specific | Structure varies by destination. Create a sync in the UI first to see the generated config shape. |
| `schedule` | Yes | object | Schedule object | Format depends on schedule type (interval, cron, etc.). Use a UI-created sync as a reference. |
| `schedulePaused` | Yes | boolean | `true`, `false` | Controls whether the sync is [enabled or disabled](https://docs.hightouch.com/syncs/overview.md#individual-sync-overview-page). |

> [!TIP]
>
> The safest way to learn the `config` structure for a destination is to create
> a sync manually in the Hightouch UI, then inspect the generated YAML file in
> your Git repository. Use that file as a template for additional syncs to the
> same destination.

> [!TIP]
>
> Avoid modifying YAML files while a sync is actively running. Pause the sync in
> the Hightouch UI before committing structural changes to prevent unexpected
> behavior.

### Customer Studio Schema

Git Sync supports version-controlling your Customer Studio schema—including parent models, related models, event models, and relationships. For full setup instructions, see [Configure Schema Git Sync](https://docs.hightouch.com/customer-studio/schema-setup.md).

> [!NOTE]
>
> Git Sync for Customer Studio schema must be enabled by Hightouch. [Contact us](#pylon-chat) to turn it on.

#### Parent model schema

[Parent models](https://docs.hightouch.com/customer-studio/schema.md#create-the-parent-model) live in the `schema/parent-models` directory. They have the same schema as [regular models](#model-schema), with the following additions:

```yaml theme={null}
primaryLabel: "primary_label_column" # Optional
secondaryLabel: "secondary_label_column" # Optional
```

#### Related model schema

[Related models](https://docs.hightouch.com/customer-studio/schema.md#add-a-related-model) live in the `schema/related-models` directory. They have the same schema as [regular models](#model-schema).

#### Event model schema

[Event models](https://docs.hightouch.com/customer-studio/schema.md#add-an-event-model) live in the `schema/event-models` directory. They have the same schema as [regular models](#model-schema), with the following addition:

```yaml theme={null}
eventTimestampColumn: "column_name" # Required
```

#### Model relationship schemas

[Model relationships](https://docs.hightouch.com/customer-studio/schema.md#understand-how-relationships-work) live in the `schema/model-relationships` directory.

The schema for model relationships depends on whether the relationship is direct or through other relationships.

Direct relationships look like this:

```yaml theme={null}
from:
  relationshipName: "" # Relationship name for from -> to
  slug: "model_slug"
  mergeColumns: false # or true
to:
  relationshipName: "" # Relationship name for to -> from
  slug: "model_slug"
  mergeColumns: false
cardinality: "1:1" # One of: "1:1", "1:many", "many:1"
mapping:
  - fromColumn: ""
    toColumn: ""
```

Through relationships look like this:

```yaml theme={null}
name: "relationship name"
from:
  slug: "model_slug"
to:
  slug: "model_slug"
through:
  # List of relationship slugs that constitute this through relationship.
  - relationship_slug_1
  - relationship_slug_2
```

### Git Sync limitations for Customer Studio schema

Git Sync currently supports version-controlling model definitions and relationship structure. However, certain schema configurations set in the Hightouch UI are not synced across environments.

For example, Git Sync does **not** copy:

* Column-level settings (for example, redaction, column suggestions, or display configuration set in the UI)
* Column aliases

The relationship-level `mergeColumns` setting is synced, because it's part of the relationship YAML.

If you migrate a schema to another environment using Git Sync, you may need to reconfigure these settings manually after deployment.

Field names are also case-sensitive in YAML. If a column name changes only in casing (for example, `user_id` to `User_Id`), Git Sync treats these as different fields. Make sure column references in your YAML files match the exact casing used in your warehouse.

→ For full setup instructions, see [Configure Schema Git Sync](https://docs.hightouch.com/customer-studio/schema-setup.md).

### Event Contract Schema

[Event contracts](https://docs.hightouch.com/events/contracts/management.md) are synced as domains, event schemas, and components. The [Events CLI](https://docs.hightouch.com/events/contracts/cli.md) can generate typed SDK wrappers from this checkout instead of calling the API. A repository uses exactly one of two on-disk layouts:

* Legacy: `events/contracts/`
* Current: `events/domains/`

Don't mix both layouts in the same repository. For new workspaces, use the domains layout.

#### Domains

Domain metadata has a simple schema:

```yaml theme={null}
name: > name of the domain
description: > optional: description for the domain
onUndeclaredSchema: > optional: one of "ALLOW_EVENT" (default) or "BLOCK_EVENT" — legacy layout only
sources: > optional: list of event-source slugs this domain is attached to
```

File locations:

* Legacy: `events/contracts/<domain-slug>.yaml`
* Current: `events/domains/<domain-slug>/domain.yaml`

In the current layout, `onUndeclaredSchema` is omitted. Undeclared-event enforcement is configured per [event source](https://docs.hightouch.com/events/contracts/violations.md#violation-enforcement), not in Git Sync YAML.

`sources` uses [event source](https://docs.hightouch.com/events/sources/overview.md) slugs (the slug on the source's settings page), not write keys or UUIDs. Inbound treats a missing `sources` key as "leave membership alone", so YAML written before this field existed will not detach sources. An empty list (`sources: []`) detaches every source from the domain. A non-empty list is a replace-set.

#### Events

[Event Schemas](https://docs.hightouch.com/events/contracts/management.md#add-an-event-contract) are YAML files that define the allowed format of events sent to Hightouch.
Each file represents a single version of a single event within a domain.

File locations:

* Legacy: `events/contracts/<domain-slug>/<event-slug>.yaml`
* Current: `events/domains/<domain-slug>/<event-slug>.yaml`

The filename is the slug of the event, which is derived from the name or type, the domain slug, and the version. Don't use the reserved slug `domain` in the current layout — it would collide with `domain.yaml`.

The schema for an event is as follows:

```yaml theme={null}
type: > one of "track", "identify", "group", "page", "screen", or "alias"
name: > for track events, the event name; otherwise should not be present
version: > optional: the version of the event; defaults to "default"
onSchemaViolation: > optional: one of "ALLOW_EVENT" (default) or "BLOCK_EVENT"
onUndeclaredFields: > optional: one of "ALLOW_EVENT" (default), "BLOCK_EVENT", or "OMIT_FIELDS"
includeBuiltInContext: > optional: one of "true" (default), or "false"
schema: |
  Schema object for validating the event, adhering to the JSON Schema spec
  Usually the root of this schema is an object which contains a nested object named properties
```

In the following example, we have a Sample Event that contains a single optional "id" field:

```yaml theme={null}
# events/domains/web/sample-event-web-default.yaml
name: Sample Event
onSchemaViolation: ALLOW_EVENT
onUndeclaredFields: ALLOW_EVENT
schema:
  type: object # The root of our schema is an object
  properties: # Objects contain a "properties" element in the JSON Schema spec
    properties: # We define a single child named properties
      type: object # Which is an object
      properties: # Finally, the properties our child object contains
        id: # Which is only an optional id field
          type: string # Which is of type string
type: track
version: default
```

This corresponds to the following JSON in the in-app JSON Editor:

```json theme={null}
{
  "type": "object",
  "properties": {
    "properties": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        }
      }
    }
  }
}
```

Because event schemas may get complex, using Hightouch [CI checks](#validate-changes) is a great way to validate your changes
before merging them into your repository.

#### Components

[Components](https://docs.hightouch.com/events/contracts/management.md#add-a-component) are reusable schema pieces. They're located at `events/domains/<domain-slug>/components/<component-slug>.yaml`.

```yaml theme={null}
name: > name of the component
version: > optional: the version of the component; defaults to "default"
description: > optional: description for the component
sources: > optional: list of event-source slugs this component applies to
schema: |
  Schema object for the component, adhering to the JSON Schema spec.
  Imports of other components are represented as $ref values inside this schema.
```

The domain slug is derived from the file path and shouldn't appear in the YAML body.

Component `sources` uses the same event-source slugs as domain membership. A missing key leaves the component's source whitelist unchanged. An empty list (`sources: []`) means the component applies to all sources (no whitelist). A non-empty list is a whitelist: the component is omitted from source-scoped reads (for example `GET /events/domains?source=`) for any other source, and `$ref`s to those omitted components are ignored in the returned event schemas.

### Manifests

Hightouch creates a manifest file at the root of your Git repository for each connected workspace. **Manifest files** list all sources and destinations in a workspace, making it easier for you to find their IDs to add to sync and model files or reference in [aliases](#aliases).

Manifest filenames follow this format: `manifest-<your-workspace-id>.yaml`. A manifest file follows this format:

```yaml theme={null}
# READ ONLY FILE
# Changes made here will be ignored. Please use the Hightouch dashboard to create and edit sources and destinations.

sources:
  production-postgres:
    name: Production postgres
    type: postgres
   snowflake-243-tenant:
    name: Snowflake EU instance
    type: snowflake
destinations:
  production-salesforce:
    name: Production Salesforce
    type: salesforce
```

Sources and destinations are listed by their ID. In the example file above, the Postgres source's ID is `production-postgres` and the Salesforce destination's ID is `production-salesforce`.

> [!WARNING]
>
> Manifest files are **read-only**. Git Sync overwrites any changes made to
> manifest files. Use the Hightouch dashboard to create and edit sources and
> destinations.

### Aliases

Managing multiple Hightouch workspaces from the same Git repository can be difficult because source and destination IDs can differ between workspaces. **Alias files** let you create custom IDs for sources and destinations in a given workspace allowing model and sync files to work across different workspaces.

Hightouch creates an empty alias file in your Git repository for each connected workspace. Alias filenames follow this format: `aliases-<your-workspace-id>.yaml`.

You can then add sources and destinations to the alias file following this format:

```yaml theme={null}
# e.g. aliases-hightouch-production-workspace.yaml
sources:
  my-source-alias: my-actual-source-id
  othersourcealias: another-workspace-source
destinations:
  my-destination-alias: existing-destination
```

Each key is an alias and each value should be an actual source or destination ID. You can find source and destination IDs in the [manifest file](#manifests) in the root of your Git repository.

> [!NOTE]
>
> Aliases should be unique across sources and destinations in the same
> workspace. In other words, you can't use the same alias for multiple resources
> in the same alias file.

#### Example alias usage

Consider a workspace, `acme-staging`, with a model `stocking-list.yaml` file like this:

```yaml theme={null}
name: Stocking list
source: staging-postgres
type: table
tableName: public.workspaces
isSchema: false
primaryKey: id
```

Another workspace called `acme-production` is connected to the same repository but can't use this model during Git Sync. That's because the `acme-production` workspace doesn't have a source with the ID `staging-postgres`. Instead, `acme-production` uses a Postgres source with the ID `production-postgres`.

You can use aliases so that both workspaces can use the same model `stocking-list.yaml` file.

1. Add an alias to the `acme-staging` workspace alias file:

```yaml theme={null}
# aliases-acme-staging.yaml
sources:
  aliased-postgres: staging-postgres
```

This creates the custom ID `aliased-postgres` that points to the source with the ID `staging-postgres`.

2. Add an alias to the `acme-production` workspace alias file:

```yaml theme={null}
# aliases-acme-production.yaml
sources:
  aliased-postgres: production-postgres
```

This creates the custom ID `aliased-postgres` that points to the source with the ID `production-postgres`. For the next step, it's important that both alias files use the same alias name. Since these aliases exist in different workspaces, they are still unique within their respective workspaces.

3. Update the `source` value of `stocking-list.yaml` to `aliased-postgres`.

```diff theme={null}
name: Stocking list
- source: staging-postgres
+ source: aliased-postgres
type: table
tableName: public.workspaces
isSchema: false
primaryKey: id
```

Now, during Git Sync, both workspaces resolve `aliased-postgres` to their respective Postgres slugs and Git Sync completes for both staging and production workspaces.

## Validate changes

You can use Hightouch CI checks to validate changes in your Git Sync repository before merging them.

To enable CI checks, turn on **GitHub Pull Request checks** in your Git Sync [configuration page](https://app.hightouch.com/extensions/git-sync/configuration?from=docs).

After CI checks are enabled, Hightouch runs a check on each pull request made to the connected Git repository. If a breaking change is detected, such as deleting a model used by a sync, the CI check fails. Open the failed check details to see which sync is affected.

If the pull request does not affect any Hightouch syncs, the CI check passes.

![Passed CI check](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/passed-code-check.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=03ac86eea689b7e7a601406d6fafac6c)

### Custom YAML validation in CI

In addition to Hightouch's built-in CI checks, you can add your own YAML validation step to your CI pipeline to catch syntax errors and missing required fields before a pull request is merged.

Here's an example using a simple validation script with `yq` (a YAML processor available in most CI environments):

```bash theme={null}
#!/bin/bash
# validate-git-sync-yaml.sh
# Validates model and sync YAML files for required fields.

set -euo pipefail

ERRORS=0

# Validate model files
for file in models/*.yaml; do
  [ -f "$file" ] || continue
  for field in name source type primaryKey; do
    if ! yq -e ".$field" "$file" > /dev/null 2>&1; then
      echo "ERROR: $file is missing required field '$field'"
      ERRORS=$((ERRORS + 1))
    fi
  done
done

# Validate sync files
for file in syncs/*.yaml; do
  [ -f "$file" ] || continue
  for field in model destination config schedule schedulePaused; do
    if ! yq -e ".$field" "$file" > /dev/null 2>&1; then
      echo "ERROR: $file is missing required field '$field'"
      ERRORS=$((ERRORS + 1))
    fi
  done
done

if [ "$ERRORS" -gt 0 ]; then
  echo "$ERRORS validation error(s) found."
  exit 1
fi

echo "All YAML files passed validation."
```

> [!TIP]
>
> This script checks for required fields only. Hightouch's built-in CI checks
> handle deeper validation, such as verifying that referenced models and
> destinations exist. Use both for the most coverage.

## Review change history

> [!TIP]
>
> Git Sync gives you a changelog through commit history. You can also use [audit
> logs](https://docs.hightouch.com/workspace-management/audit-logs.md) to review changes in Hightouch.

You can use your Git repository to see who created or modified supported resources and when.

> [!NOTE]
>
> To show the correct user for Hightouch-created commits, the user's Hightouch
> email must match their GitHub email.

In GitHub, open the repository's **Commits** tab or file history to review changes.

![Commit logs in GitHub](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-audit-log.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=7a2e22eadfd137f40d9355df8e874c6d)

Open a commit to see the exact line-by-line diff.

![File diff in GitHub](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-audit-log-detail.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=9707c8b8ef0c473ca3d933ce7ddac19c)

## Use tunneling for private Git repositories

If your Git repository is hosted on an on-premise server without public internet access, use [tunneling](https://docs.hightouch.com/security/ssh-tunneling.md) to connect Hightouch to your Git server.

1. Go to the [**Tunnels** tab](https://app.hightouch.com/settings/tunnels?from=docs) on the **Settings** page.
2. Create a tunnel or reverse tunnel that connects to your Git server. For more information, see the [tunneling documentation](https://docs.hightouch.com/security/ssh-tunneling.md).
3. Go to the [Git Sync configuration page](https://app.hightouch.com/extensions/git-sync/configuration?from=docs).
4. Under **Configuration > Git credentials**, select **Manage**.
5. Add your Git credential and select the correct tunnel.

> [!NOTE]
>
> We recommend using the SSH protocol and `ssh_privatekey` to connect to private Git repositories.
>
> Basic authentication can have issues forwarding authentication headers through an SSH tunnel and HTTP redirects. For more information about SSH authentication, see the [GitHub SSH documentation](https://docs.github.com/en/authentication/connecting-to-github-with-ssh).
>
> Hightouch does not support `ssh_privatekey` with passphrases.

![Entering Git credentials in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-repo-ssh-tunnel.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=f101e25c0b6fcb67c603ac808aab05e5)

## Next steps

After Git Sync is configured, you can use Git workflows to manage supported Hightouch resources. For example, you can:

* Create a new model by copying an existing model file and updating the name and query
* Create a new model and sync in the same pull request
* Use [CI checks](#validate-changes) to validate changes before merging them
* Use [aliases](#aliases) to share model and sync files across staging and production workspaces

## FAQ

### What does the Full resync button do?

Selecting **Full resync** runs an outbound sync for all supported resources in the workspace. This pushes all supported resources from Hightouch to Git, then pulls changes from Git back to your workspace.

![Git Sync diagram](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/integrations/git-sync-outbound.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=2b944ea40d60aa57ae33347c4e6aa595)

If your Git repository has changes that have not synced to Hightouch yet, **Full resync** can overwrite those changes.

### Recreate a deleted YAML file

Git Sync does not support deletions from Git. If you delete a YAML file in Git, the corresponding model or sync remains in Hightouch. The file is not recreated in Git until the next change is made in Hightouch or you trigger a [full resync](#what-does-the-full-resync-button-do).

To recreate a deleted YAML file, select **Full resync**.

You can also recreate the file manually by adding a YAML file with the same filename and [schema](#schemas) as the original file. Hightouch recognizes that the file refers to an existing model or sync and associates it with the existing resource.

### Protected branch update failed

If you selected a protected branch, Git Sync setup might fail with this error:

```
Protected branch update failed - Changes must be made through a pull request
```

To resolve the error, use one of these options:

* Select a branch that is not protected, then open pull requests in GitHub to merge updates into the protected branch.
* Follow the instructions in [Use Git Sync with branch protection](#use-git-sync-with-branch-protection) to let the Hightouch Connect app work with your protected branch.
