Merge Source Guard
Date:October 22, 2026Author:Nicolas GallagherCategory:ENGINEERINGMerges take a new optional field: expected_source_sha. Set it to the source commit that you
reviewed. The merge then uses that commit, not the current tip of source_ref.
Without the field, a merge reads the source branch when it starts. If the branch moves after you preview the merge, the merge can include commits that you did not review.
A merge preview returns source_tip_sha and target_tip_sha. Send them as expected_source_sha
and expected_target_sha:
curl "$PIERRE_API_BASE_URL/repos/my-repo/merge" \
-H "Authorization: Bearer $PIERRE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_ref": "feature/preview",
"expected_source_sha": "9eb378bdb5bf1944f6ba0bd5a2c4df3ce32f4df1",
"target_branch": "main",
"expected_target_sha": "c4f0fdfc41adab56630b34f5f4fd4e84a2c5b4d2",
"strategy": "merge",
"commit_message": "Merge feature/preview",
"author": { "name": "Merge Bot", "email": "merge-bot@example.com" }
}'If the source branch advances and still contains the commit, the merge uses the commit that you
sent. If the branch no longer contains it, for example after a force push or a delete, the merge
returns 409 and changes nothing:
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "source branch moved: the source branch does not contain commit 9eb378bdb5bf1944f6ba0bd5a2c4df3ce32f4df1",
"code": "conflict",
"error": "source branch moved: the source branch does not contain commit 9eb378bdb5bf1944f6ba0bd5a2c4df3ce32f4df1"
}The detail of a failed source guard starts with source branch moved:. Use it to tell this
failure from the other 409 responses of a merge.
See the API reference for repos.merge and repos.previewMerge.