- name
- git-integration-operations-cli
- description
- Runs the Fabric Git integration lifecycle through fab api or az rest, including connecting a workspace to Azure DevOps or GitHub, committing, updating from Git, reading sync status, resolving conflicts, disconnecting a connected workspace, and automating sync with a service principal. For stage promotion use deployment-pipelines-authoring-cli. Branch switching, fab deploy, fabric-cicd and cross-workspace rebinding are out of scope.
- metadata
- {"maturity":"experimental","owner":"yaron"}
> **Telemetry — MANDATORY.** Every `api.fabric.microsoft.com` call must carry
> `x-ms-fabric-skill: git-integration-operations-cli` (`az rest`: `--headers "x-ms-fabric-skill=git-integration-operations-cli"`),
> including every LRO poll, `fabric_lro` and retry. Snippets omit it — add it anyway.
> **CRITICAL NOTES**
> 1. To find a workspace ID from its name: list all workspaces, then filter by
> `displayName` with JMESPath. To find an item ID: list items of that type in the
> workspace, then filter by `displayName`.
> 2. All Git operations are **control plane** calls against
> `https://api.fabric.microsoft.com`. Run `fab auth login` once and `fab api`
> sets the base URL and Fabric token audience automatically. With the `az rest`
> fallback you must pass the matching `--resource` yourself, or you get a 401.
> 3. `commitToGit`, `updateFromGit`, and even `git/status` can return **HTTP 202
> (long-running operation)**, not just a synchronous body. On `202`, poll the
> operation: with `fab`, read `x-ms-operation-id` from `--show_headers` and poll
> `fab api operations/{id}` until `status` is `Succeeded`; with the `az rest`
> fallback, poll the `Location` / `x-ms-operation-id` header (see COMMON-CORE
> long-running operation polling). THEN read `git/status` to confirm
> `workspaceHead == remoteCommitHash`. Prefer the operation poll to confirm
> completion; if you poll `git/status` instead, treat an in-progress (`202`) or
> not-yet-synced response as "still running" and do not act on its `changes`
> until the heads match. (Learn: rest/api/fabric/core/git and
> fabric/cicd/git-integration/git-automation.)
> 4. `updateFromGit` requires the workspace's **current** `workspaceHead`; a stale
> value returns `400 WorkspaceHeadMismatch`. Always read `git/status` first.
> 5. **Prerequisites for every Git operation:** the workspace must be assigned to a
> **capacity** (an unassigned workspace fails with `WorkspaceHasNoCapacityAssigned`),
> and the caller (user or service principal) must hold the right workspace
> role: **connect and disconnect require Admin**; **commit and update require at
> least Contributor** with write permission on all items; **branch switching
> requires Admin** (or Contributor when the workspace opt-in setting *Allow users
> with at least Contributor role to change Git branch* is on). Only one Git
> operation may run against a workspace at a time.
> 6. **Tenant admin switches** gate Git integration and are a common cause of an
> unexplained failure. The **GitHub** sync switch is **off by default** (enable it
> before any GitHub connect); Azure DevOps sync is on by default. Cross-region
> workspace-to-*Azure*-repo needs the **cross-geo export** switch (GitHub does not
> enforce it). See [references/git-integration-concepts.md § Tenant admin prerequisites](references/git-integration-concepts.md#tenant-admin-prerequisites).
# Git Integration Operations — CLI Skill
Automate the Fabric Git integration lifecycle (connect, commit, update, status,
disconnect) from CLI environments. Whether cross-workspace item references rebind
after promotion is a separate concern, determined by the item definition format
(logical vs object IDs) rather than the Git lifecycle itself.
## Prerequisite Knowledge
Read these shared references first (paths assume this skill lives under
`skills/git-integration-operations-cli/` in `skills-for-fabric`; adjust when drafting
outside the repo):
- [COMMON-CLI.md § Finding Workspaces and Items in Fabric](../../common/COMMON-CLI.md#finding-workspaces-and-items-in-fabric) — **Mandatory** for resolving workspace/item IDs by name.
- [COMMON-CORE.md § Authentication & Token Acquisition](../../common/COMMON-CORE.md#authentication--token-acquisition) — wrong audience = 401; read before any auth issue.
- [COMMON-CLI.md § Authentication Recipes](../../common/COMMON-CLI.md#authentication-recipes) — `az login` flows and token acquisition for CLI.
- [COMMON-CLI.md § Fabric Control-Plane API via az rest](../../common/COMMON-CLI.md#fabric-control-plane-api-via-az-rest) — the `az rest` **fallback** path (**always pass `--resource`**); includes LRO polling helpers.
- [COMMON-CORE.md § Core Control-Plane REST APIs](../../common/COMMON-CORE.md#core-control-plane-rest-apis) — pagination, long-running-operation polling, rate limiting.
## Table of Contents
| Task | Reference |
|---|---|
| Pre-flight checks before connecting | [SKILL.md § Pre-Flight (before you connect)](#pre-flight-before-you-connect) |
| Connect a workspace to Git | [SKILL.md § Connect a Workspace to Git](#connect-a-workspace-to-git) |
| Create a git connection (service principal) | [SKILL.md § Create the Git provider connection (service principal)](#create-the-git-provider-connection-service-principal) |
| Commit workspace items to Git | [SKILL.md § Commit to Git](#commit-to-git) |
| Update a workspace from Git | [SKILL.md § Update from Git](#update-from-git) |
| Check sync status | [SKILL.md § Check Sync Status](#check-sync-status) |
| Resolve update conflicts | [SKILL.md § Resolve Conflicts](#resolve-conflicts) |
| Disconnect from Git | [SKILL.md § Disconnect from Git](#disconnect-from-git) |
| Link a branch workspace to its base (workspace relations) | [SKILL.md § Link a Branch Workspace to its Base (Workspace Relations)](#link-a-branch-workspace-to-its-base-workspace-relations) |
| Git integration concepts (sync model, status, permissions) | [references/git-integration-concepts.md § Concepts](references/git-integration-concepts.md#concepts) |
| Tenant admin prerequisites (switches) | [references/git-integration-concepts.md § Tenant admin prerequisites](references/git-integration-concepts.md#tenant-admin-prerequisites) |
| Supported Git providers | [references/git-integration-concepts.md § Supported Git providers](references/git-integration-concepts.md#supported-git-providers) |
| Supported item types | [references/git-integration-concepts.md § Supported item types](references/git-integration-concepts.md#supported-item-types) |
| Avoiding formatting-only diffs (trailing newline / line endings) | [references/git-integration-concepts.md § Avoiding formatting-only diffs](references/git-integration-concepts.md#avoiding-formatting-only-diffs) |
| Service principal / CI-CD pipeline templates | [references/automation-templates.md](references/automation-templates.md) |
| Gotchas, Rules, Troubleshooting | [SKILL.md § Gotchas, Rules, Troubleshooting](#gotchas-rules-troubleshooting) |
## Must/Prefer/Avoid
### MUST DO
- Read `git/status` and pass the current `workspaceHead` into every `commitToGit`
and `updateFromGit` call.
- Poll `git/status` after each async operation until
`workspaceHead == remoteCommitHash` **and** the `changes` array is empty. A
`202`/`Succeeded` status is not proof of a correct sync — validate the state.
- Deploy an item together with the items it references in the same commit, so
logical IDs can resolve inside the target workspace.
### MUST NEVER
- Never run two Git operations concurrently on the same workspace — a second
`commitToGit`/`updateFromGit` while one is still `Running` corrupts head
tracking. Serialize: poll the first to `Succeeded` before starting the next.
- Never treat a workspace as both a source of truth **and** a CI/CD push target
on the same branch. Pick one writer per branch: either humans commit from the
workspace, or automation pushes to Git — not both at once.
### PREFER
- `fab api` (after `fab auth login`) as the **primary** driver: it sets the base
URL and Fabric token audience automatically, avoiding the 401 wrong-audience
trap. Use `az rest` with `--resource "https://api.fabric.microsoft.com"` as the
**fallback** when `fab` is unavailable or you need fine-grained header control.
- `PreferRemote` conflict resolution for a clean "pull latest from Git" into a
downstream/target workspace.
### AVOID
- Reusing a stale `workspaceHead` (causes `400 WorkspaceHeadMismatch`).
- Passing request bodies via stdin to `fab api` (use `-i <file.json>` or `-i '<inline JSON>'`).
---
## CLI: `fab` primary, `az rest` fallback
Every operation below is shown with `fab api`. Run `fab auth login` once (it
supports service principals for automation); `fab` then handles the base URL and
token audience for you. The identical call on the `az rest` fallback is a
mechanical translation:
**Authenticate `fab` for the identity you are running as:**
```bash
fab auth login # interactive user (SSO)
fab auth login -u <client-id> -p <client-secret> --tenant <tenant-id> # service principal (CI/CD)
fab auth login --identity # managed identity (Azure compute)
```
A service principal or managed identity must be workspace **Admin** to connect or
disconnect (at least **Contributor** with write on all items is enough for
commit/update) and (for the `ConfiguredConnection` path) hold the git provider
credentials. See
[references/automation-templates.md](references/automation-templates.md) for full
pipeline scripts.
- `fab api <endpoint>` becomes `az rest --method GET --url "https://api.fabric.microsoft.com/v1/<endpoint>" --resource "https://api.fabric.microsoft.com"`.
- `fab api -X post <endpoint> -i body.json` adds `--method POST --headers "Content-Type=application/json" --body @body.json`.
`fab api` reads a request body via `-i` from either a file path or an inline JSON
string (e.g. `-i '{"displayName":"..."}'`) — never from stdin. It prints
`{"status_code": <code>, "text": <parsed body>}` (add `--show_headers` for a
top-level `headers` object), so read response fields with `jq -r '.text.<field>'`
and the LRO id with `jq -r '.headers["x-ms-operation-id"]'`. See
[Example 2](#example-2-az-rest-fallback-equivalents) for a full `az rest` pair.
---
## Pre-Flight (before you connect)
Run these Git-relevant checks in order before `git/connect`. Each failure has a
specific fix — resolving them up front avoids the most common connect-time errors.
| Check | Command | Pass = proceed / Fail = fix |
|---|---|---|
| Auth + Fabric token | `fab auth login` then `fab api workspaces` | `401` → re-login (`fab` sets the correct audience automatically) |
| Caller is workspace Admin | `fab api "workspaces/${WORKSPACE_ID}/roleAssignments"` | Connect/disconnect require the **Admin** role |
| Workspace has capacity | `fab api "workspaces/${WORKSPACE_ID}"` → check `capacityId` | `WorkspaceHasNoCapacityAssigned` → assign a capacity (Git requires one) |
| Git credential for the SP path | `fab api connections` (reuse if one already fits) | SP connect needs a `ConfiguredConnection`. If none exists, the skill **creates** it from your ADO org/project/repo + SP tenant/client/secret — see [Create the Git provider connection](#create-the-git-provider-connection-service-principal) (needs the tenant toggle "service principals can create connections"). The interactive-user SSO path uses `Automatic` and needs no connection. |
| Target `directoryName` exists on the branch | GitHub: `curl -fsS -H "Authorization: Bearer $PAT" "https://api.github.com/repos/$OWNER/$REPO/contents/$DIR?ref=$BRANCH"` — ADO: Items API on the repo | The API (unlike the portal) does **not** create a missing folder — connecting to a `directoryName` that doesn't exist fails with `404 GitProviderResourceNotFound`. If the folder is new, pre-create it first (see the note under [Connect a Workspace to Git](#connect-a-workspace-to-git)). |
## Connect a Workspace to Git
Connect a workspace to an Azure DevOps or GitHub repo, then initialize the
connection to establish the sync direction.
```bash
# 1. Connect (Azure DevOps example). Body goes in a file, passed with -i.
cat > connect.json <<'JSON'
{
"gitProviderDetails": {
"gitProviderType": "AzureDevOps",
"organizationName": "myorg",
"projectName": "MyProject",
"repositoryName": "fabric",
"branchName": "main",
"directoryName": "/workspace-a"
}
}
JSON
fab api -X post "workspaces/${WORKSPACE_ID}/git/connect" -i connect.json
# 2. Initialize the connection (choose which side wins on first sync)
printf '{"initializationStrategy":"PreferWorkspace"}' > init.json
fab api -X post "workspaces/${WORKSPACE_ID}/git/initializeConnection" -i init.json
```
Use `PreferWorkspace` when the workspace already has the authoritative items;
`PreferRemote` when the Git branch is authoritative.
> **The target `directoryName` must already exist on the branch.** Unlike the Fabric
> portal — which silently creates a missing folder by committing a `README.md`
> placeholder into it — the `git/connect` **API does not create the folder**.
> Connecting to a `directoryName` that does not exist on the branch fails with
> `404 GitProviderResourceNotFound`. The repo and branch must exist too (connect
> never creates a branch). If the folder is new, create it first by committing a
> placeholder file to the branch **through the Git provider's API** (not a Fabric
> API), then connect. Match the portal's convention with a `README.md` whose body is
> `This is an auto-created file via Fabric skills for <workspace-name>`.
>
> ```bash
> # GitHub: create <DIR>/README.md on <BRANCH> (reuse the PAT behind the Git connection)
> DIR="workspace-a"; WS_NAME="My Workspace"
> CONTENT=$(printf 'This is an auto-created file via Fabric skills for %s\n' "$WS_NAME" | base64)
> cat > mkfolder.json <<JSON
> { "message": "Create ${DIR} folder for Fabric Git connect", "branch": "${BRANCH}", "content": "${CONTENT}" }
> JSON
> curl -fsS -X PUT -H "Authorization: Bearer ${GITHUB_PAT}" \
> "https://api.github.com/repos/${OWNER}/${REPO}/contents/${DIR}/README.md" -d @mkfolder.json
> # Azure DevOps equivalent: POST .../_apis/git/repositories/${REPO}/pushes with an
> # "add" change for "/${DIR}/README.md" on refs/heads/${BRANCH} (same body content).
> ```
> **Two authentication modes (both providers use `myGitCredentials` for automation).**
> A workspace authenticates to git in one of two ways:
> - **Automatic git credential** (Azure DevOps only): the connecting user's SSO
> identity, established from workspace settings. Secondary contributors with at
> least Contributor reuse it. Not available for GitHub, and not usable for
> headless / service-principal automation.
> - **Configured credential** (Azure DevOps *and* GitHub): a Fabric cloud connection
> referenced in the connect body. This is the required path for any service
> principal or unattended run:
> ```json
> "myGitCredentials": { "source": "ConfiguredConnection", "connectionId": "<connection-guid>" }
> ```
> For **Azure DevOps**, the connection's `credentialType` can be either
> `ServicePrincipal` (headless automation) or `OAuth2` (a stored delegated user
> token); both are valid ConfiguredConnection credentials per the Create Connection
> API and both support multitenant scenarios.
> For **GitHub**, connecting a workspace **requires a Personal Access Token (PAT)**:
View on GitHub