关于 git-integration-operations-cli
通过命令行管理 Fabric 工作区与 Git 仓库集成生命周期的技能,适用于执行同步和自动化的操作者。它支持连接、提交、更新、状态检查与断开,并强调异步操作轮询和工作区前置条件。
主要功能
- 01连接工作区与 Git 仓库
- 02提交和更新工作区内容
- 03检查同步状态并处理冲突
- 04断开 Git 集成
- 05支持服务主体和 CI/CD 流程
适用场景
- 01将 Fabric 工作区连接到 Azure DevOps 或 GitHub
- 02把工作区项目提交至 Git
- 03从 Git 更新工作区并确认同步完成
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ skills-for-fabric --skill git-integration-operations-cli安装后在 Claude Code 中输入/git-integration-operations-cli确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
apm.ymlreferences/automation-templates.mdgit-integration-concepts.md详细介绍
遥测 — 强制要求。 每次
api.fabric.microsoft.com调用都必须携带x-ms-fabric-skill: git-integration-operations-cli(az rest:--headers "x-ms-fabric-skill=git-integration-operations-cli"), 包括每次 LRO 轮询、fabric_lro和重试。代码片段中省略了此项,但仍须添加。
重要说明
- 要根据工作区名称查找工作区 ID:列出所有工作区,然后使用 JMESPath 按
displayName筛选。要查找项目 ID:列出该工作区中此类型的所有项目,然后按displayName筛选。- 所有 Git 操作都是针对
https://api.fabric.microsoft.com的控制平面调用。运行一次fab auth login,然后fab api会自动设置基 URL 和 Fabric 令牌受众。使用az rest作为备用方式时,必须自行传入匹配的--resource,否则会收到 401。commitToGit、updateFromGit,甚至git/status都可能返回 HTTP 202 (长时间运行操作),而非同步响应正文。收到202时,轮询该操作:使用fab时,从--show_headers读取x-ms-operation-id,并轮询fab api operations/{id},直到status为Succeeded;使用az rest作为备用方式时,轮询Location/x-ms-operation-id标头(参见 COMMON-CORE 长时间运行操作轮询)。然后读取git/status,确认workspaceHead == remoteCommitHash。优先通过操作轮询确认完成;如果改为轮询git/status, 则将进行中(202)或尚未同步的响应视为“仍在运行”,在两端的 head 匹配之前,不要对其changes采取操作。(了解详情:rest/api/fabric/core/git 和 fabric/cicd/git-integration/git-automation。)updateFromGit需要工作区的当前workspaceHead;过期值会返回400 WorkspaceHeadMismatch。务必先读取git/status。- 每项 Git 操作的前提条件: 工作区必须已分配到容量(未分配的工作区会返回
WorkspaceHasNoCapacityAssigned),且调用者(用户或服务主体)必须拥有合适的工作区 角色:连接和断开连接需要 Admin;提交和更新需要至少 Contributor,并且对所有项目都 拥有写入权限;切换分支需要 Admin(如果启用了工作区选择设置 允许至少具有 Contributor 角色的用户更改 Git 分支,则需要 Contributor)。同一时间每个工作区只能运行一项 Git 操作。- 租户管理员开关会控制 Git 集成,也常常导致原因不明的失败。GitHub 同步开关默认关闭(连接 GitHub 前必须启用);Azure DevOps 同步默认开启。跨区域工作区连接到 Azure 存储库需要启用跨地理区域导出开关(GitHub 不受此限制)。参见 references/git-integration-concepts.md § 租户管理员前提条件。
Git 集成操作 — CLI 技能
在 CLI 环境中自动执行 Fabric Git 集成生命周期(连接、提交、更新、状态、断开连接)。升级后,跨工作区项目引用是否会重新绑定是另一个问题,取决于项目定义格式(逻辑 ID 还是对象 ID),而非 Git 生命周期本身。
前置知识
请先阅读以下共享参考资料(假设此技能位于 skills-for-fabric 中的
skills/git-integration-operations-cli/ 目录下;在仓库外撰写时请相应调整路径):
- COMMON-CLI.md § 在 Fabric 中查找工作区和项目 — 按名称解析工作区/项目 ID 时必须阅读。
- COMMON-CORE.md § 身份验证和令牌获取 — 受众错误会导致 401;遇到任何身份验证问题前请先阅读。
- COMMON-CLI.md § 身份验证操作指南 —
az login流程和 CLI 令牌获取。 - COMMON-CLI.md § 通过 az rest 调用 Fabric 控制平面 API —
az rest备用路径(始终传入--resource);包含长时间运行操作(LRO)的轮询辅助程序。 - COMMON-CORE.md § 核心控制平面 REST API — 分页、长时间运行操作轮询、速率限制。
目录
| 任务 | 参考 |
|---|---|
| 连接前的预检 | SKILL.md § 预检(连接前) |
| 将工作区连接到 Git | SKILL.md § 将工作区连接到 Git |
| 创建 Git 连接(服务主体) | SKILL.md § 创建 Git 提供商连接(服务主体) |
| 将工作区项目提交到 Git | SKILL.md § 提交到 Git |
| 从 Git 更新工作区 | SKILL.md § 从 Git 更新 |
| 检查同步状态 | SKILL.md § 检查同步状态 |
| 解决更新冲突 | SKILL.md § 解决冲突 |
| 断开与 Git 的连接 | SKILL.md § 断开与 Git 的连接 |
| 将分支工作区关联到其基础工作区(工作区关系) | SKILL.md § 将分支工作区关联到其基础工作区(工作区关系) |
| Git 集成概念(同步模型、状态、权限) | references/git-integration-concepts.md § 概念 |
| 租户管理员前提条件(开关) | references/git-integration-concepts.md § 租户管理员前提条件 |
| 支持的 Git 提供商 | references/git-integration-concepts.md § 支持的 Git 提供商 |
| 支持的项目类型 | references/git-integration-concepts.md § 支持的项目类型 |
| 避免仅因格式产生差异(尾随换行符 / 行尾) | references/git-integration-concepts.md § 避免仅因格式产生差异 |
| 服务主体 / CI-CD 流水线模板 | references/automation-templates.md |
| 注意事项、规则、故障排除 | SKILL.md § 注意事项、规则、故障排除 |
必须 / 优先 / 避免
必须执行
- 读取
git/status,并在每次调用commitToGit和updateFromGit时传入当前的workspaceHead。 - 每次异步操作后轮询
git/status,直到workspaceHead == remoteCommitHash且changes数组为空。202/Succeeded状态并不能证明同步正确 — 请验证状态。 - 部署某个项目时,应在同一次提交中一并部署其所引用的项目,以便在目标工作区中解析逻辑 ID。
必须遵守
- 绝不能在同一工作区中并发运行两个 Git 操作——当一个操作仍处于
Running状态时,再启动第二个commitToGit/updateFromGit会破坏头部跟踪。应串行执行:先轮询第一个操作,直到状态变为Succeeded,再启动下一个操作。 - 绝不能在同一分支上同时将工作区视为事实来源和 CI/CD 推送目标。每个分支只能有一个写入方:要么由人工从工作区提交,要么由自动化推送到 Git,不能同时进行。
建议
- 将
fab api(在fab auth login后使用)作为主要驱动方式:它会自动设置基本 URL 和 Fabric 令牌受众,避免遇到 401 受众不匹配的问题。如果fab不可用,或需要精细控制标头,则将az rest与--resource "https://api.fabric.microsoft.com"搭配使用作为备用方案。 - 使用
PreferRemote冲突解决策略,将 Git 中的最新内容干净地“拉取”到下游/目标工作区。
避免
- 重用过期的
workspaceHead(会导致400 WorkspaceHeadMismatch)。 - 通过标准输入向
fab api传递请求正文(请使用-i <file.json>或-i '<inline JSON>')。
CLI:以 fab 为主,az rest 为备用
以下每个操作都使用 fab api 展示。运行一次 fab auth login(支持用于自动化的服务主体);之后 fab 会为你处理基本 URL 和令牌受众。使用 az rest 作为备用方案时,相同调用只需进行机械式转换:
为当前运行身份验证 fab:
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)
服务主体或托管标识必须是工作区 Admin,才能连接或断开连接(对于提交/更新,只需具备至少 Contributor 权限,并对所有项目拥有写入权限);此外,(对于 ConfiguredConnection 路径)还必须持有 Git 提供程序凭据。完整管道脚本请参阅
references/automation-templates.md。
fab api <endpoint>对应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对应添加--method POST --headers "Content-Type=application/json" --body @body.json。
fab api 使用 -i 从文件路径或内嵌 JSON 字符串(例如 -i '{"displayName":"..."}')读取请求正文——绝不能从标准输入读取。它会输出 {"status_code": <code>, "text": <parsed body>}(添加 --show_headers 可在顶层输出 headers 对象),因此可通过 jq -r '.text.<field>' 读取响应字段,并通过 jq -r '.headers["x-ms-operation-id"]' 读取 LRO ID。完整的 az rest 调用对照示例请参阅示例 2。
连接前检查
在执行 git/connect 前,按顺序运行这些与 Git 相关的检查。每项检查失败都有对应的解决方法——提前解决问题可避免最常见的连接错误。
| 检查 | 命令 | 通过 = 继续 / 失败 = 修复 |
|---|---|---|
| 身份验证 + Fabric 令牌 | fab auth login 然后执行 fab api workspaces | 401 → 重新登录(fab 会自动设置正确的受众) |
| 调用者是工作区管理员 | fab api "workspaces/${WORKSPACE_ID}/roleAssignments" | 连接/断开连接需要 Admin 角色 |
| 工作区有容量 | fab api "workspaces/${WORKSPACE_ID}" → 检查 capacityId | WorkspaceHasNoCapacityAssigned → 分配容量(Git 需要容量) |
| SP 路径的 Git 凭据 | fab api connections(如果已有合适的连接,请重复使用) | SP 连接需要 ConfiguredConnection。如果没有,技能会使用你的 ADO 组织/项目/存储库以及 SP 租户/客户端/密钥创建连接——请参阅创建 Git 提供程序连接(需要启用租户开关“服务主体可以创建连接”)。交互式用户 SSO 路径使用 Automatic,无需连接。 |
目标分支上存在 directoryName | GitHub:curl -fsS -H "Authorization: Bearer $PAT" "https://api.github.com/repos/$OWNER/$REPO/contents/$DIR?ref=$BRANCH" — ADO:存储库上的 Items API | API(与门户不同)不会创建缺失的文件夹——连接到不存在的 directoryName 会失败,并显示 404 GitProviderResourceNotFound。如果文件夹是新建的,请先预先创建(请参阅将工作区连接到 Git下的说明)。 |
将工作区连接到 Git
将工作区连接到 Azure DevOps 或 GitHub 存储库,然后初始化连接以确定同步方向。
# 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
当工作区已包含权威项目时,使用 PreferWorkspace;当 Git 分支是权威来源时,使用 PreferRemote。
目标
directoryName必须已存在于分支上。 与 Fabric 门户不同,后者会通过向缺失的文件夹提交一个README.md占位文件来静默创建文件夹,而git/connectAPI 不会创建文件夹。连接到分支上不存在的directoryName会失败,并返回404 GitProviderResourceNotFound。存储库和分支也必须已存在(connect 不会创建分支)。如果文件夹是新的,请先通过 Git 提供商的 API(而不是 Fabric API)向分支提交一个占位文件,然后再连接。请遵循门户的惯例,使用正文为This is an auto-created file via Fabric skills for <workspace-name>的README.md。# GitHub:在 <BRANCH> 上创建 <DIR>/README.md(复用 Git 连接使用的 PAT) 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 等效操作:向 .../_apis/git/repositories/${REPO}/pushes 发送 POST, # 在 refs/heads/${BRANCH} 上为 "/${DIR}/README.md" 添加一个 "add" 更改(正文内容相同)。
两种身份验证模式(两个提供商在自动化场景中都使用
myGitCredentials)。 工作区通过以下两种方式之一向 Git 进行身份验证:
- 自动 Git 凭据(仅限 Azure DevOps):连接用户的 SSO 身份,基于工作区设置建立。至少具有 Contributor 权限的其他贡献者可以复用该身份。GitHub 不提供此模式,且此模式不适用于无头自动化或服务主体自动化。
- 已配置凭据(适用于 Azure DevOps 和 GitHub):请求正文中引用的 Fabric 云连接。这是任何服务主体或无人值守运行所需的方式:
对于 Azure DevOps,连接的"myGitCredentials": { "source": "ConfiguredConnection", "connectionId": "<connection-guid>" }credentialType可以是ServicePrincipal(无头自动化)或OAuth2(已存储的委托用户令牌);根据 Create Connection API 的规定,两者都是有效的 ConfiguredConnection 凭据,并且都支持多租户场景。 对于 GitHub,连接工作区必须使用个人访问令牌(PAT):云连接存储一个具有 Contents Read 权限(以及提交所需的 Write 权限)的细粒度令牌,或一个具有repo范围的经典令牌。GitHub 没有自动凭据模式,因此即使是交互式用户连接也必须使用 PAT。 区别并不是“GitHub 需要凭据,ADO 不需要”。当服务主体发起连接时,ADO 同样需要 ConfiguredConnection;自动模式仅适用于交互式用户 SSO。(了解更多: fabric/cicd/git-integration/automate-git-integration-with-service-principal; rest/api/fabric/core/git/connect。)
处理初始化响应。
initializeConnection会返回requiredAction(UpdateFromGit、CommitToGit或None),以及workspaceHead和remoteCommitHash,并且本身也可能返回202。操作完成后,请使用返回的哈希值调用requiredAction指定的同步操作,否则工作区虽然已连接,但仍未完全同步。(了解更多: rest/api/fabric/core/git/initialize-connection。)
创建 Git 提供商连接(服务主体)
对于无人值守自动化,请先创建一个 Fabric 云连接,用于存储 Git 提供商凭据,然后将其 id 作为 connectionId 传入上文的 myGitCredentials 块中。使用
Create Connection API
(POST /v1/connections,委托范围 Connection.ReadWrite.All)。只有在 Fabric 管理员设置“服务主体可以创建工作区、连接和部署管道”已启用时,服务主体才能创建连接。
必须提供的信息(Azure DevOps):
- ADO 组织、项目和存储库名称,组合为一个
url参数:https://dev.azure.com/<org>/<project>/_git/<repo>/ - 服务主体的租户 ID、客户端(应用程序)ID和客户端密钥
- 连接显示名称(最多 200 个字符,在租户中必须唯一)
**连接生效前的先决条件:**授予服务主体 (a) 对该 Azure DevOps 组织和项目的访问权限(Git 访问权限由 ADO 自身强制执行,并非仅由 Fabric 控制),以及 (b) 目标 Fabric 工作区的 Admin 权限。
# Create an Azure DevOps source-control connection using a service principal.
# The body holds a cleartext SP secret, so restrict perms and delete it on exit
# (a leftover connection.json can be captured by a later CI step or artifact upload).
trap 'rm -f connection.json' EXIT
umask 077
cat > connection.json <<'JSON'
{
"displayName": "ado-sp-fabric-cicd",
"connectivityType": "ShareableCloud",
"connectionDetails": {
"type": "AzureDevOpsSourceControl",
"creationMethod": "AzureDevOpsSourceControl.Contents",
"parameters": [
{ "dataType": "Text", "name": "url",
"value": "https://dev.azure.com/<org>/<project>/_git/<repo>/" }
]
},
"credentialDetails": {
"credentials": {
"credentialType": "ServicePrincipal",
"tenantId": "<tenant-id>",
"servicePrincipalClientId": "<client-id>",
"servicePrincipalSecret": "<client-secret>"
}
}
}
JSON
fab api -X post "connections" -i connection.json
201 响应会返回连接 id;将其用作连接调用中 myGitCredentials 块的 connectionId。如果要完全避免明文密钥,可以使用 -i '{...}' 以内联方式传入请求体(不在磁盘上创建文件),也可以通过 servicePrincipalSecretReference 引用 Key Vault 密钥,而非使用原始的 servicePrincipalSecret。上面的示例使用 credentialType: "ServicePrincipal";Azure DevOps 连接也接受 credentialType: "OAuth2"(存储的委托用户令牌),且这两种凭据类型都支持多租户场景。若要将现有的用户连接工作区切换为服务主体,请将 SP 添加为工作区 Admin,分享或重新创建此连接,然后使用相同的 ConfiguredConnection 请求体调用
PATCH /workspaces/{id}/git/myGitCredentials。(了解更多:
fabric/cicd/git-integration/automate-git-integration-with-service-principal;
rest/api/fabric/core/connections/create-connection。)
提交到 Git
将工作区项目推送到已连接的分支。先读取头信息,然后提交。
# Read current head. git/status can return 202 (LRO) with no body — capture the
# status code with --show_headers and poll before reading, or workspaceHead comes
# back as "null" and the commit below fails with a misleading 400.
RESP=$(fab api "workspaces/${WORKSPACE_ID}/git/status" --show_headers)
if [ "$(echo "$RESP" | jq -r '.status_code')" = "202" ]; then
OP=$(echo "$RESP" | jq -r '.headers["x-ms-operation-id"]')
until [ "$(fab api "operations/${OP}" | jq -r '.text.status')" = "Succeeded" ]; do sleep 5; done
RESP=$(fab api "workspaces/${WORKSPACE_ID}/git/status" --show_headers) # now 200 with body
fi
HEAD=$(echo "$RESP" | jq -r '.text.workspaceHead')
# Commit all pending changes. Build the body with jq so the comment is always
# valid JSON even if it contains a quote, backslash, or newline.
jq -n --arg head "$HEAD" --arg msg "Author items" \
'{mode:"All", workspaceHead:$head, comment:$msg}' > commit.json
RESP=$(fab api -X post "workspaces/${WORKSPACE_ID}/git/commitToGit" -i commit.json --show_headers)
# commitToGit may complete synchronously (200) or async (202). Only poll when async.
if [ "$(echo "$RESP" | jq -r '.status_code')" = "202" ]; then
OP=$(echo "$RESP" | jq -r '.headers["x-ms-operation-id"]')
until [ "$(fab api "operations/${OP}" | jq -r '.text.status')" = "Succeeded" ]; do sleep 5; done
fi
使用 "mode": "Selective" 和 items 数组提交部分项目。commitToGit 会同步完成(200,操作已结束),或异步完成(202,带有用于轮询的 x-ms-operation-id)——应像上文一样根据 .status_code 进行判断,而不要假定一定返回 202。
从 Git 更新
将已提交的项目拉取到工作区(例如,连接到相同分支/文件夹的下游或目标工作区)。
# Read both hashes from the target workspace
STATUS=$(fab api "workspaces/${TARGET_WS}/git/status")
TH=$(echo "$STATUS" | jq -r '.text.workspaceHead')
REMOTE=$(echo "$STATUS" | jq -r '.text.remoteCommitHash')
cat > update.json <<JSON
{
"workspaceHead": "${TH}",
"remoteCommitHash": "${REMOTE}",
"conflictResolution": {
"conflictResolutionType": "Workspace",
"conflictResolutionPolicy": "PreferRemote"
},
"options": { "allowOverrideItems": true }
}
JSON
fab api -X post "workspaces/${TARGET_WS}/git/updateFromGit" -i update.json
检查同步状态
持续轮询,直到工作区和远程头信息一致。commitToGit 和 updateFromGit 都是异步操作,因此可以通过此循环确认它们已完成。
for i in $(seq 1 30); do
S=$(fab api "workspaces/${WORKSPACE_ID}/git/status")
WH=$(echo "$S" | jq -r '.text.workspaceHead')
RC=$(echo "$S" | jq -r '.text.remoteCommitHash')
CH=$(echo "$S" | jq -r '.text.changes | length')
# Success = heads match AND no residual changes. Heads matching alone can still
# leave uncommitted/unapplied diffs; validate both before declaring "synced".
[ "$WH" = "$RC" ] && [ "$CH" = "0" ] && { echo "synced at $WH"; break; }
sleep 8
done
如果操作仍在进行中(提交/更新返回 202),请直接轮询 LRO,而不要根据头信息猜测:
# Capture x-ms-operation-id from the 202, then poll until Succeeded
OP_ID=$(fab api -X post "workspaces/${WORKSPACE_ID}/git/commitToGit" -i commit.json \
--show_headers | jq -r '.headers["x-ms-operation-id"]')
while :; do
ST=$(fab api "operations/${OP_ID}" | jq -r '.text.status')
case "$ST" in Succeeded) break ;; Failed) echo "op failed"; break ;; *) sleep 5 ;; esac
done
git/status 还会返回一个 changes 数组——非空数组表示存在尚未提交的(工作区)或尚未应用的(远程)差异。
解决冲突
真正的冲突是指双方都进行了更改:上次同步后,工作区中的同一项(未提交的本地编辑)和分支中的同一项(新提交)都发生了修改。git/status 会在该项的 changes 数组中将其标记为 conflictType: "Conflict"(workspaceChange 和 remoteChange 均显示 Modified)。这与单边差异不同;对于单边差异,只需使用 updateFromGit(仅远程更改)或 commitToGit(仅工作区更改),无需指定策略。
当双方都修改了某项时,updateFromGit 必须指定冲突策略——不带 conflictResolution 块调用时会返回 400 MissingWorkspaceConflictResolution:
conflictResolutionPolicy: "PreferRemote"— 以 Git 分支为准(通常用于拉取到目标环境);本地编辑将被丢弃。conflictResolutionPolicy: "PreferWorkspace"— 以当前工作区为准;若要将工作区版本推送到 Git,请改用commitToGit,它不接受冲突策略。
设置 options.allowOverrideItems: true,以允许更新覆盖目标工作区中已存在的项(只要冲突项已存在于工作区中,就必须设置此选项;对于双方都进行更改的冲突,按定义该项必然已存在于工作区中)。
如何选择策略:
| 场景 | 策略 |
|---|---|
| 下游/目标工作区从 Git 拉取最新内容 | PreferRemote |
| 创作工作区是事实来源 | PreferWorkspace(或使用 commitToGit 将工作区版本推送到 Git) |
| 同一项发生真正的双向编辑 | 检查 git/status 中每个 changes 条目的 conflictType,并逐项审慎解决 |
断开与 Git 的连接
fab api -X post "workspaces/${WORKSPACE_ID}/git/disconnect"
将分支工作区关联到其基础工作区(工作区关系)
预览版。 Git 工作区关系 API 属于 Fabric Git 接口的一部分,目前为预览版。它们会记录分支工作区与其分支来源的基础工作区之间的沿袭关系,因此可在 Fabric 门户中查看基础工作区↔分支工作区的关系。如果分支工作区是在内置分支功能发布之前创建的,或者分支工作区由你自己的自动化(而非门户中的分支功能)预配,且你仍希望在 UI 中显示这种沿袭关系,请使用这些 API。创建关系不会将任一工作区连接到 Git、移动项或启动同步;它只会记录关联关系。
前提条件 — 两个工作区必须指向同一个 Git 根目录。 除非基础工作区和分支工作区连接到同一个存储库根目录且仅分支不同,否则创建操作会失败并返回 WorkspaceRelationRootDirectoryMismatch:
- Azure DevOps:
organizationName、projectName、repositoryName和directoryName必须相同 — 只有branchName不同。 - GitHub: 存储库 URL 和
directoryName必须相同 — 只有branchName不同。
因此,请先将两个工作区连接到 Git(并完成初始化);关系仅用于显示现有的分支/基础工作区关系。
创建关系(链接分支 ↔ 基础工作区)
该关系是双向的,因此只需创建一次。请仔细阅读正文——你 POST 到的工作区和 relationType 是两回事:
- URL 中的工作区(
workspaces/{workspaceId}/...)是你发起调用的工作区。 relatedWorkspaceId是另一个工作区。relationType描述的是另一个(relatedWorkspaceId)工作区的角色,而不是 URL 中的工作区。创建时仅Base和Branch有效。
因此,同一个链接有两种等效的创建方式:
| 调用来源(URL 工作区) | relatedWorkspaceId | relationType |
|---|---|---|
| 分支工作区 | 基础工作区 | Base |
| 基础工作区 | 分支 | Branch |
不要两种方式都执行——某个方向的关系一旦存在,再创建另一个方向就会失败,并返回 WorkspaceRelationBidirectionalExists。无论从哪一侧调用,所需权限都相同:在分支工作区上拥有 Admin 权限,在基础工作区上拥有 Contributor 或更高权限。推荐(也最直观)的方式是从分支发起调用,并指向基础工作区:
# From the BRANCH workspace, declare that the related workspace is its Base.
printf '{"relatedWorkspaceId":"%s","relationType":"Base"}' "${BASE_WS}" > relation.json
fab api -X post "workspaces/${BRANCH_WS}/git/workspaceRelations" -i relation.json
返回 201。支持服务主体和托管标识。
列出关系
任何拥有 Viewer 或更高权限的用户都可以列出工作区的关系(通过 continuationToken 分页)。响应中的 relationType 可以是 Base、Branch 或 RelatedWorkspace。
fab api "workspaces/${WORKSPACE_ID}/git/workspaceRelations"
删除关系
仅移除谱系链接——不会删除任一工作区或其中的项目。调用者必须在路径中指定的工作区(基础工作区或分支工作区)上拥有 Admin 权限。只有 Branch 关系可以删除——否则 API 会返回 WorkspaceRelationTypeNotBranch。
fab api -X delete "workspaces/${WORKSPACE_ID}/git/workspaceRelations/${RELATION_ID}"
(了解更多:rest/api/fabric/core/workspace-relations。)
相关:跨工作区项目绑定
从同一分支更新目标工作区会将项目提升到该工作区。项目之间的引用是自动重新绑定(逻辑 ID)还是失效(对象 ID / 硬编码终结点),取决于项目定义格式,而非 Git 生命周期;这同样适用于 fabric-cicd 和 Bulk Import API。要预测特定依赖关系的结果,请阅读带证据标签的兼容性矩阵;若要实证验证不确定的引用,请使用 cross-workspace-item-binding 技能(计划中的配套技能,目前尚未在此仓库中提供)——这是与本技能驱动的连接 / 提交 / 更新生命周期不同的问题。
相关:Terraform(基础设施即代码)
若要以声明式方式管理连接部分(基础设施即代码),而不是使用命令式 fab api 调用,请使用 git-integration-terraform 技能(计划中的配套技能,目前尚未在此仓库中提供)。它涵盖 microsoft/fabric 提供程序的 fabric_connection 和 fabric_workspace_git 资源(Azure DevOps 服务主体或自动模式、GitHub PAT、初始化策略、plan/apply/import)。在这种模式下,Terraform 管理连接,而本技能中的提交 / 从 Git 更新 / 状态操作仍采用命令式运行时路径——因此常见做法是用 Terraform 管理连接,再通过 CLI 步骤执行从 Git 更新。
相关:Variable Library(环境参数化)
Git 集成会在工作区之间移动项目定义,但它本身不会参数化那些必须随环境阶段(dev/test/prod)变化的值——连接字符串、终结点、容量 ID、功能标志。每个环境的参数化由 Variable Library 负责:只需定义一次变量,为每个阶段提供一组值,并让项目解析适用于该阶段的值。当 Git 同步的工作区需要使用随环境变化的值时,请使用配套的 variable-library-cli 技能对变量 / 值集进行建模,而不要在此处硬编码或重复实现该逻辑——本技能负责源代码控制生命周期,Variable Library 技能负责为其提供所需的参数化。
注意事项、规则和故障排除
| 症状 | 原因 | 修复方法 |
|---|---|---|
400 WorkspaceHeadMismatch | workspaceHead 已过期 | 重新读取 git/status,使用当前的头信息重试 |
400 MissingWorkspaceConflictResolution | 两侧修改了同一项,但发送 updateFromGit 时未提供 conflictResolution | 添加 conflictResolution(策略为 PreferRemote 或 PreferWorkspace)+ options.allowOverrideItems: true;先检查 git/status 中是否有 conflictType: "Conflict" |
WorkspaceHasNoCapacityAssigned | 工作区未分配容量(Git 需要容量) | 为工作区分配容量,然后重试 |
git/connect 返回 404 GitProviderResourceNotFound | 提供商上不存在该仓库、分支或 directoryName——与门户不同,API 不会自动创建文件夹 | 验证仓库和分支名称;如果文件夹是新建的,请先通过 Git 提供商 API 向分支提交一个 README.md 占位文件来预创建文件夹(参见将工作区连接到 Git),然后重试 |
WorkspaceAlreadyConnectedToGit | 工作区已关联到仓库 | 先断开连接,再连接到新仓库 |
InsufficientPrivileges / 403 | 调用方缺少操作所需的角色(连接/断开需要 Admin;提交/更新需要至少具有所有项目写入权限的 Contributor) | 为调用方授予所需角色:连接/断开需要 Admin,提交/更新需要具有所有项目写入权限的 Contributor |
PrincipalTypeNotSupported | 服务主体使用了自动凭据(或 GitHub) | 在 myGitCredentials 中切换为 ConfiguredConnection |
使用 fab api 时出现 commitToGitRequest is required | 请求正文通过标准输入传入 | 将请求正文写入文件,并传入 -i body.json |
操作返回 202,随后没有变化 | 异步操作尚未完成 | 轮询 operations/{id}(从 --show_headers 获取),或轮询 git/status,直到各头信息匹配 |
操作长时间停留在 Running | 工作区较大,或另一个 Git 操作正在排队 | 使用合理的超时时间持续轮询(约 10 分钟);切勿同时启动第二个 Git 操作 |
401 Unauthorized | Token 的受众不正确 | 运行 fab auth login(fab 会自动设置受众);使用 az rest 作为备用方案时,传入 --resource https://api.fabric.microsoft.com |
git/workspaceRelations 返回 WorkspaceRelationRootDirectoryMismatch | 基础工作区和分支工作区连接到了不同的 Git 根目录 | 将两者连接到相同的仓库/组织/项目/目录(Azure DevOps),或相同的仓库 URL/目录(GitHub),仅 branchName 不同,然后重试 |
WorkspaceRelationAlreadyExists / WorkspaceRelationBidirectionalExists | 基础工作区↔分支工作区的关系已记录(该关系是双向的) | 无需操作——使用 GET git/workspaceRelations 确认;不要再创建反向关系 |
删除时出现 WorkspaceRelationTypeNotBranch | 尝试删除的关系不是 Branch 关系 | 只有 Branch 关系可以删除——使用正确的 workspaceRelationId,并从分支侧删除 |
| 每次同步都报告你未曾进行的未提交更改(例如删除了一个空行,或 CRLF/LF 格式切换) | 受影响的工作区中,编辑器或 AI Agent 创建了带有末尾换行符或 CRLF 的项目源文件(notebook-content.py、pipeline-content.json、.platform),随后 Fabric 将其重新序列化为观察到的导出格式(LF,无末尾换行符) | 通过有代表性的往返操作确认该行为,然后在编写时匹配观察到的导出格式:不添加末尾换行符,使用 LF 换行。使用 .editorconfig + .gitattributes 在同步的仓库中固定该格式,并告知 AI Agent 不要添加末尾换行符。参见 git-integration-concepts.md § 避免纯格式差异 |
示例
示例 1:提交源代码,然后部署到目标工作区
# Commit everything in the source workspace (jq builds a JSON-safe body)
SRC_HEAD=$(fab api "workspaces/${SRC_WS}/git/status" | jq -r '.text.workspaceHead')
jq -n --arg head "$SRC_HEAD" --arg msg "Deploy v1" \
'{mode:"All", workspaceHead:$head, comment:$msg}' > commit.json
fab api -X post "workspaces/${SRC_WS}/git/commitToGit" -i commit.json
# ...poll src status until synced, then updateFromGit into ${TARGET_WS} (see above)
示例 2:az rest 回退方案
当 fab 不可用时,可以通过 az rest 调用相同的 REST 终端节点。始终传入 --resource;请求正文可以内联指定(--body '{...}'),也可以从文件读取
(--body @file.json):
# status
az rest --method GET \
--url "https://api.fabric.microsoft.com/v1/workspaces/${WORKSPACE_ID}/git/status" \
--resource "https://api.fabric.microsoft.com"
# commit (body inline)
az rest --method POST \
--url "https://api.fabric.microsoft.com/v1/workspaces/${WORKSPACE_ID}/git/commitToGit" \
--resource "https://api.fabric.microsoft.com" \
--headers "Content-Type=application/json" \
--body "{\"mode\":\"All\",\"workspaceHead\":\"${HEAD}\",\"comment\":\"Author items\"}"