Integration
Git integration in SXL Studio: syncing tokens and data with GitHub and GitLab, Push, Pull, branches, and connection setup.
Why Git integration
Git integration turns SXL Studio into a two-way bridge between Figma and the code repository. Designers update tokens in the plugin → Push → developers receive changes. Developers update JSON → designer Pulls → designs update automatically.
What gets synchronized
| Data | Repository path |
|---|---|
| Data (Database) | dataPath/ — JSON, CSV files, mappings, images |
| Tokens | tokensPath/ — token JSON files, config.json, diff-id, code-connect.json |
Supported providers
- GitHub — github.com and GitHub Enterprise Server (GHES)
- GitLab — gitlab.com and self-hosted instances
Git integration works through the provider's REST API (not git CLI). Connection uses a Personal Access Token.
Setting up a connection
Step 1 — Create a connection
- Open the Sync panel in the plugin (icon in the bottom bar)
- Click New Sync
- Fill in the fields:
| Field | Description | Example |
|---|---|---|
| Provider | GitHub or GitLab | github |
| Name | Connection name (for you) | Design System |
| Repository | Repository path | org/design-system |
| Branch | Working branch | main |
| Access Token | Personal Access Token | ghp_xxxx... |
| Data Path | Data folder in the repo | data |
| Tokens Path | Tokens folder in the repo | tokens |
| Enterprise URL | URL for self-hosted (optional) | https://git.company.com |
- Click Save
Step 2 — First Pull
After creating the connection, perform a Pull — the plugin will download all files from the specified folders.
Push — sending changes
What happens during Push
- The plugin collects all local changes:
- Data (datasets, mappings, images)
- Tokens (JSON files,
config.json,diff-id,code-connect.json,diff-grid.*)
- Compares with the remote state (by SHA)
- Forms a single commit with changes
- Sends to the selected branch
How to Push
- Click the Push button in the bottom bar (or via the Git menu)
- The Commit Modal opens with an embedded inline side-by-side diff:
- Left — list of changed files with status icons (
+/~/×) and counts - Right — Monaco DiffEditor: Remote on the left, Local on the right. Click files in the list to switch views. Binary assets are marked in the list and skipped by the diff engine
- Hunk navigation: F7 — next change, Shift+F7 — previous (or the arrow buttons in the top-right corner of the editor)
- Below — the commit message field with provider and branch hints
- Left — list of changed files with status icons (
- Enter a commit description
- Click Push
All text files are normalized to a trailing newline (POSIX convention). This eliminates "phantom" diffs on blank last lines when there are no semantic changes. Files that turn out identical after normalization are marked in the list with
≈and open with an explanation instead of the DiffEditor — so the Push list stays honest.
If there's nothing to send, the plugin shows a notification. If there are tokens but no Tokens Path is set, it shows an error with guidance.
Scope changed
If since the last Pull you changed the connection's repository, branch, dataPath, or tokensPath, the Commit Modal shows a ⚠ Scope changed badge. Push is not blocked by an extra acknowledgement checkbox—read the warning and decide; usually you should Pull into the new scope first so you don't push with stale context against a different branch.
Pull — receiving changes
Soft Pull vs Hard Pull
| Type | When | What it does |
|---|---|---|
| Soft Pull | Repeat pull in the same scope (branch, repo, paths unchanged) | Downloads only changed files. Local data is preserved |
| Hard Pull | First pull, branch/repo/path change, or explicit request | Clears all local data and downloads fresh |
How to Pull
- Click the Pull button in the bottom bar
- The Pull Modal opens with an embedded inline side-by-side diff:
- Left — list of incoming files with counts and statuses
- Right — Monaco DiffEditor: Local on the left (current state), Remote on the right (what will arrive). For new files the left side is empty — that's exactly what "added" looks like
- Navigation: F7 / Shift+F7 between changes, arrow buttons in the top-right corner
- Below you can see the last remote-branch commit (author, message, time)
- Click Pull
Hard Pull
When you need a full resync:
- Use Hard Pull from the menu
- All local data and tokens will be replaced with repository contents
Progress
Pull shows progress through phases:
listing— scanning the file treedata— downloading datatokens— downloading tokensfinalizing— finalizingdone— complete
Speed with many files
Data and token files are downloaded in parallel (a bounded number of concurrent Git requests) to speed up Pull on large trees without hammering the provider. For huge repos, tree listing and network latency may still dominate; narrow dataPath / tokensPath in the connection settings if needed.
Auto-export after Pull
If autoExportOnPull: true is set in config.json, token export to Figma Variables runs automatically after Pull.
Branches
Switching branches
- Click the current branch name in the bottom bar
- A menu shows available branches
- Select the desired branch
- The plugin updates the connection setting
- Perform Pull to synchronize
Switching branches does not download files automatically — you need to Pull. The first Pull after switching is a Hard Pull.
Creating a branch
- Click the branch name → Create Branch
- Enter the new branch name
- Optionally: select a base branch (Create Branch From…)
- The plugin creates the branch via the provider's API
Status indicators
The plugin's bottom bar shows:
| Indicator | Meaning |
|---|---|
| Provider icon + branch | Current active connection |
| 🟢 Green dot on Pull | Changes available on remote |
| 🟢 Green dot on Push | Local changes pending |
| Spinner | Operation in progress (pull/push/refresh) |
| Progress counter | Pull progress (downloaded / total) |
| ⚠ Truncated tree banner | GitHub returned a partial tree (repo too large) — Push/Pull are blocked until dataPath / tokensPath is narrowed |
| ⚠ Hard pull required (in the Pull modal) | repo / branch / dataPath / tokensPath changed — the next Pull will wipe local data |
Status auto-refresh
- Background polling — every 60 seconds, but it's not a rigid
setInterval: after any refresh (background, manual, or from local events) the next tick is rescheduled. This removes duplicates like "I pressed Refresh → the plugin fires another request 5 seconds later" - Conditional GET. GitHub uses
ETag/If-None-Match→ a304 Not Modifiedresponse doesn't consume the primary rate limit. GitLab checks the branch head commit SHA in one lightweight request → if the SHA hasn't changed, the client reuses the cached tree without paginating through files. Idle cost is close to zero - Instant refresh on focus. When the plugin window becomes visible again (
visibilitychange → visible/focus), the plugin triggerscheck-git-statuswith a 10-second throttle — this makes the Pull indicator react to external pushes without noisy round-trips - Any manual Refresh, as well as background polling and visibility triggers, bypass the tree TTL cache while keeping the conditional check (ETag / head-SHA). This means a fresh branch commit is visible to the plugin on the next tick — rather than after two missed ticks as it used to behave
- Local operations (save, export, push, pull) trigger
triggerGitStatusRefresh()in-process — no UI roundtrips - The
GitClientis cached for 60 seconds by(connection, repo, branch, paths, token-signature), so a single Push or Pull doesn't re-fetch the tree multiple times - Each refresh publishes the fresh tree snapshot into the UI store immediately — the inline diff in Push/Pull modals opens without extra loading and reflects current changes, even if you never opened the Git Browser
Performance and resilience
- Truncated tree protection. For very large monorepos, the GitHub API may return a partial tree (
truncatedflag). In that case SXL Studio cannot guarantee a complete list of deletions, so Push and Pull are disabled and a warning banner is shown in the bottom bar. Fix: narrowdataPath/tokensPathto the actual design-system folders - Trailing newline normalization. All text files (JSON, CSV, Vue, Code Connect) are normalized to a single trailing
\nbefore push. This removes "phantom" diffs that appear when different editors add or strip the final blank line - Real deletions only. A file is marked as "deleted" in the Push modal only if it actually exists on remote. This prevents false diffs after local renames/cleanups
- Full diff for configs. The inline diff covers not only tokens and datasets, but also
code-connect.json,diff-id.*,diff-grid.*,config.json— so every change that goes into the commit is visible - Size guard for huge files. Files larger than 1 MB or over 10 000 lines aren't loaded into the inline DiffEditor — an "Inline diff disabled" placeholder is shown instead. The file still ends up in the commit; it's just easier to review in an IDE or the provider's UI. This keeps the plugin iframe from freezing on heavy JSON
- Noop filter. If the local and remote versions are byte-identical after normalization, the file is still committed (its Git blob SHA differs), but no DiffEditor opens for it — a short placeholder is shown instead. Such files are flagged in the list with a
≈marker
Conflicts
SXL Studio does not use git merge. Synchronization works via SHA blob API:
- During Pull — local files are overwritten with remote contents
- During Push — if the remote already has a newer commit, the API may return an error
Recommendation: use a standard workflow — one person Pushes, others Pull. For parallel work, use separate branches.
Related sections
- GitHub Setup — step-by-step GitHub guide
- GitLab Setup — step-by-step GitLab guide
- Export Variables & Styles — auto-export after Pull