@@ -4,7 +4,6 @@ import * as github from '@actions/github'
44import { GitHub , getOctokitOptions } from '@actions/github/lib/utils.js'
55import { throttling } from '@octokit/plugin-throttling'
66import * as path from 'path'
7- import * as fs from 'fs/promises'
87
98import config from './config.js'
109
@@ -200,13 +199,6 @@ export default class Git {
200199 }
201200 }
202201
203- async getBlobBase64Content ( file ) {
204- const fileRelativePath = path . join ( this . workingDir , file )
205- const fileContent = await fs . readFile ( fileRelativePath )
206-
207- return fileContent . toString ( 'base64' )
208- }
209-
210202 async getLastCommitSha ( ) {
211203 this . lastCommitSha = await execCmd (
212204 `git rev-parse HEAD` ,
@@ -243,56 +235,70 @@ export default class Git {
243235 }
244236
245237 // Returns a git tree parsed for the specified commit sha
246- async getTree ( commitSha ) {
247- const output = await execCmd (
248- `git ls-tree -r --full-tree ${ commitSha } ` ,
238+ async getTreeId ( commitSha ) {
239+ core . debug ( `Getting treeId for commit ${ commitSha } ` )
240+ const output = ( await execCmd (
241+ `git cat-file -p ${ commitSha } ` ,
249242 this . workingDir
250- )
243+ ) ) . split ( '\n' )
251244
252- const tree = [ ]
253- for ( const treeObject of output . split ( '\n' ) ) {
254- const [ mode , type , sha ] = treeObject . split ( / \s / )
255- const file = treeObject . split ( '\t' ) [ 1 ]
256-
257- const treeEntry = {
258- mode,
259- type,
260- sha,
261- path : file
262- }
263-
264- tree . push ( treeEntry )
265- }
245+ const commitHeaders = output . slice ( 0 , output . findIndex ( ( e ) => e === '' ) )
246+ const tree = commitHeaders . find ( ( e ) => e . startsWith ( 'tree' ) ) . replace ( 'tree ' , '' )
266247
267248 return tree
268249 }
269250
270- // Creates the blob objects in GitHub for the files that are not in the previous commit only
271- async createGithubBlobs ( commitSha ) {
272- core . debug ( 'Creating missing blobs on GitHub' )
273- const [ previousTree , tree ] = await Promise . all ( [ this . getTree ( `${ commitSha } ~1` ) , this . getTree ( commitSha ) ] )
274- const promisesGithubCreateBlobs = [ ]
275-
276- for ( const treeEntry of tree ) {
277- // If the current treeEntry are in the previous tree, that means that the blob is uploaded and it doesn't need to be uploaded to GitHub again.
278- if ( previousTree . findIndex ( ( entry ) => entry . sha === treeEntry . sha ) !== - 1 ) {
279- continue
280- }
281-
282- const base64Content = await this . getBlobBase64Content ( treeEntry . path )
251+ async getTreeDiff ( referenceTreeId , differenceTreeId ) {
252+ const output = await execCmd (
253+ `git diff-tree ${ referenceTreeId } ${ differenceTreeId } -r` ,
254+ this . workingDir
255+ )
283256
284- // Creates the blob. We don't need to store the response because the local sha is the same and we can use it to reference the blob
285- const githubCreateBlobRequest = this . github . git . createBlob ( {
286- owner : this . repo . user ,
287- repo : this . repo . name ,
288- content : base64Content ,
289- encoding : 'base64'
257+ const diff = [ ]
258+ for ( const line of output . split ( '\n' ) ) {
259+ const splitted = line
260+ . replace ( / ^ : / , '' )
261+ . replace ( '\t' , ' ' )
262+ . split ( ' ' )
263+
264+ const [
265+ newMode ,
266+ previousMode ,
267+ newBlob ,
268+ previousBlob ,
269+ change ,
270+ path
271+ ] = splitted
272+
273+ diff . push ( {
274+ newMode,
275+ previousMode,
276+ newBlob,
277+ previousBlob,
278+ change,
279+ path
290280 } )
291- promisesGithubCreateBlobs . push ( githubCreateBlobRequest )
292281 }
293282
294- // Wait for all the file uploads to be completed
295- await Promise . all ( promisesGithubCreateBlobs )
283+ return diff
284+ }
285+
286+ // Creates the blob objects in GitHub for the files that are not in the previous commit only
287+ async uploadGitHubBlob ( blob ) {
288+ core . debug ( `Uploading GitHub Blob for blob ${ blob } ` )
289+ const fileContent = await execCmd (
290+ `git cat-file -p ${ blob } ` ,
291+ this . workingDir ,
292+ false
293+ )
294+
295+ // Creates the blob. We don't need to store the response because the local sha is the same and we can use it to reference the blob
296+ return this . github . git . createBlob ( {
297+ owner : this . repo . user ,
298+ repo : this . repo . name ,
299+ content : Buffer . from ( fileContent ) . toString ( 'base64' ) ,
300+ encoding : 'base64'
301+ } )
296302 }
297303
298304 // Gets the commit list in chronological order
@@ -313,25 +319,10 @@ export default class Git {
313319 )
314320 }
315321
316- // Returns an array of objects with the git tree and the commit, one entry for each pending commit to push
317- async getCommitsDataToPush ( ) {
318- const commitsToPush = await this . getCommitsToPush ( )
319-
320- const commitsData = [ ]
321- for ( const commitSha of commitsToPush ) {
322- const [ commitMessage , tree ] = await Promise . all ( [ this . getCommitMessage ( commitSha ) , this . getTree ( commitSha ) , this . createGithubBlobs ( commitSha ) ] )
323- const commitData = {
324- commitMessage,
325- tree
326- }
327- commitsData . push ( commitData )
328- }
329- return commitsData
330- }
331-
332322 // A wrapper for running all the flow to generate all the pending commits using the GitHub API
333323 async createGithubVerifiedCommits ( ) {
334- const commitsData = await this . getCommitsDataToPush ( )
324+ core . debug ( `Creating Commits using GitHub API` )
325+ const commits = await this . getCommitsToPush ( )
335326
336327 if ( SKIP_PR === false ) {
337328 // Creates the PR branch if doesn't exists
@@ -350,8 +341,8 @@ export default class Git {
350341 }
351342 }
352343
353- for ( const commitData of commitsData ) {
354- await this . createGithubTreeAndCommit ( commitData . tree , commitData . commitMessage )
344+ for ( const commit of commits ) {
345+ await this . createGithubCommit ( commit )
355346 }
356347
357348 core . debug ( `Updating branch ${ SKIP_PR === false ? this . prBranch : this . baseBranch } ref` )
@@ -502,14 +493,43 @@ export default class Git {
502493 } )
503494 }
504495
505- async createGithubTreeAndCommit ( tree , commitMessage ) {
496+ async createGithubCommit ( commitSha ) {
497+ const [ treeId , parentTreeId , commitMessage ] = await Promise . all ( [
498+ this . getTreeId ( `${ commitSha } ` ) ,
499+ this . getTreeId ( `${ commitSha } ~1` ) ,
500+ this . getCommitMessage ( commitSha )
501+ ] )
502+
503+ const treeDiff = await this . getTreeDiff ( treeId , parentTreeId )
504+ core . debug ( `Uploading the blobs to GitHub` )
505+ const blobsToCreate = treeDiff
506+ . filter ( ( e ) => e . newMode !== '000000' ) // Do not upload the blob if it is being removed
507+
508+ await Promise . all ( blobsToCreate . map ( ( e ) => this . uploadGitHubBlob ( e . newBlob ) ) )
506509 core . debug ( `Creating a GitHub tree` )
510+ const tree = treeDiff . map ( ( e ) => {
511+ if ( e . newMode === '000000' ) { // Set the sha to null to remove the file
512+ e . newMode = e . previousMode
513+ e . newBlob = null
514+ }
515+
516+ const entry = {
517+ path : e . path ,
518+ mode : e . newMode ,
519+ type : 'blob' ,
520+ sha : e . newBlob
521+ }
522+
523+ return entry
524+ } )
525+
507526 let treeSha
508527 try {
509528 const request = await this . github . git . createTree ( {
510529 owner : this . repo . user ,
511530 repo : this . repo . name ,
512- tree
531+ tree,
532+ base_tree : parentTreeId
513533 } )
514534 treeSha = request . data . sha
515535 } catch ( error ) {
0 commit comments