11import {
22 createCommitOnBranchQuery ,
3- createRefMutation ,
43 getRepositoryMetadata ,
5- updateRefMutation ,
64} from "./github/graphql/queries.js" ;
7- import type {
8- CreateCommitOnBranchMutationVariables ,
9- GetRepositoryMetadataQuery ,
10- } from "./github/graphql/generated/operations.js" ;
5+ import type { GetRepositoryMetadataQuery } from "./github/graphql/generated/operations.js" ;
116import {
127 CommitFilesFromBase64Args ,
138 CommitFilesResult ,
@@ -47,6 +42,18 @@ const getOidFromRef = (
4742 return ref . target . oid ;
4843} ;
4944
45+ const isAlreadyExistingRefError = (
46+ error : unknown ,
47+ ) =>
48+ typeof error === "object" &&
49+ error !== null &&
50+ "status" in error &&
51+ "message" in error &&
52+ typeof error . status === "number" &&
53+ typeof error . message === "string" &&
54+ error . status === 422 &&
55+ error . message . includes ( "Reference already exists" ) ;
56+
5057const createCommit = async ( {
5158 octokit,
5259 refId,
@@ -121,7 +128,6 @@ export const commitFilesFromBase64 = async ({
121128 const baseOid = getOidFromRef ( base , info . baseRef ) ;
122129 const targetOid = info . targetBranch ?. target ?. oid ?? null ;
123130 const sameBranchBase = "branch" in base && base . branch === branch ;
124- const repositoryId = info . id ;
125131
126132 let mode : "create" | "update" | "force-update" ;
127133
@@ -140,39 +146,110 @@ export const commitFilesFromBase64 = async ({
140146 ) ;
141147 }
142148
143- let refId : string ;
149+ if ( mode === "force-update" ) {
150+ // Use a stable temp branch name so a later run can recover and reuse it
151+ // if an earlier run failed before cleanup completed.
152+ const tempBranch = `changesets-ghcommit-temp/${ branch } ` ;
144153
145- if ( mode === "create" ) {
146- const createdRef = await createRefMutation ( octokit , {
147- input : {
148- repositoryId,
149- name : `refs/heads/${ branch } ` ,
150- oid : baseOid ,
151- } ,
154+ let tempRefId : string ;
155+
156+ try {
157+ const createdTempRef = await octokit . rest . git . createRef ( {
158+ owner,
159+ repo,
160+ ref : `refs/heads/${ tempBranch } ` ,
161+ sha : baseOid ,
162+ } ) ;
163+
164+ const refIdStr = createdTempRef . data . node_id ;
165+
166+ if ( ! refIdStr ) {
167+ throw new Error ( `Failed to create temporary branch ${ tempBranch } ` ) ;
168+ }
169+
170+ tempRefId = refIdStr ;
171+ } catch ( error ) {
172+ if ( ! isAlreadyExistingRefError ( error ) ) {
173+ throw error ;
174+ }
175+
176+ const updatedTempRef = await octokit . rest . git . updateRef ( {
177+ owner,
178+ repo,
179+ ref : `heads/${ tempBranch } ` ,
180+ sha : baseOid ,
181+ force : true ,
182+ } ) ;
183+
184+ const refIdStr = updatedTempRef . data . node_id ;
185+
186+ if ( ! refIdStr ) {
187+ throw new Error ( `Failed to update temporary branch ${ tempBranch } ` ) ;
188+ }
189+
190+ tempRefId = refIdStr ;
191+ }
192+
193+ await log ?. debug ( `Creating commit on branch ${ tempBranch } ` ) ;
194+ const tempCommit = await createCommit ( {
195+ octokit,
196+ refId : tempRefId ,
197+ baseOid,
198+ message,
199+ fileChanges,
152200 } ) ;
153201
154- const refIdStr = createdRef . createRef ?. ref ?. id ;
202+ const tempRefTarget = tempCommit . createCommitOnBranch ?. ref ?. target ;
203+ const tempHeadOid =
204+ tempRefTarget ?. __typename === "Commit" ? tempRefTarget . oid : null ;
155205
156- if ( ! refIdStr ) {
157- throw new Error ( `Failed to create branch ${ branch } ` ) ;
206+ if ( ! tempHeadOid ) {
207+ throw new Error (
208+ `Failed to determine head commit of temporary branch ${ tempBranch } ` ,
209+ ) ;
158210 }
159211
160- refId = refIdStr ;
161- } else if ( mode === "force-update" ) {
162- const updatedRef = await updateRefMutation ( octokit , {
163- input : {
164- refId : sameBranchBase ? resolvedBaseRef ! . id : info . targetBranch ! . id ,
165- oid : baseOid ,
166- force : true ,
167- } ,
212+ const updatedTargetRef = await octokit . rest . git . updateRef ( {
213+ owner,
214+ repo,
215+ ref : `heads/${ branch } ` ,
216+ sha : tempHeadOid ,
217+ force : true ,
168218 } ) ;
169219
170- const refIdStr = updatedRef . updateRef ?. ref ?. id ;
220+ const updatedTargetRefId = updatedTargetRef . data . node_id ;
171221
172- if ( ! refIdStr ) {
222+ if ( ! updatedTargetRefId ) {
173223 throw new Error ( `Failed to update branch ${ branch } ` ) ;
174224 }
175225
226+ await octokit . rest . git . deleteRef ( {
227+ owner,
228+ repo,
229+ ref : `heads/${ tempBranch } ` ,
230+ } ) ;
231+
232+ return {
233+ refId : updatedTargetRefId ,
234+ } ;
235+ }
236+
237+ let refId : string ;
238+
239+ if ( mode === "create" ) {
240+ const createdRef = await octokit . rest . git . createRef ( {
241+ owner,
242+ repo,
243+ ref : `refs/heads/${ branch } ` ,
244+ sha : baseOid ,
245+ } ) ;
246+
247+ const refIdStr = createdRef . data . node_id ;
248+
249+ if ( ! refIdStr ) {
250+ throw new Error ( `Failed to create branch ${ branch } ` ) ;
251+ }
252+
176253 refId = refIdStr ;
177254 } else {
178255 refId = sameBranchBase ? resolvedBaseRef ! . id : info . targetBranch ! . id ;
0 commit comments