From fa545b74a7f4750a29e56556055a622ac48db540 Mon Sep 17 00:00:00 2001 From: Peter Evans <18365890+peter-evans@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:48:18 +0900 Subject: [PATCH] stash and restore uncommitted changes --- __test__/create-or-update-branch.int.test.ts | 92 ++++++++++++++++++++ dist/index.js | 28 +++++- src/create-or-update-branch.ts | 10 ++- src/git-command-manager.ts | 17 ++++ 4 files changed, 141 insertions(+), 6 deletions(-) diff --git a/__test__/create-or-update-branch.int.test.ts b/__test__/create-or-update-branch.int.test.ts index bfbda30..09fa8ae 100644 --- a/__test__/create-or-update-branch.int.test.ts +++ b/__test__/create-or-update-branch.int.test.ts @@ -1150,6 +1150,50 @@ describe('create-or-update-branch tests', () => { expect(await gitLogMatches([INIT_COMMIT_MESSAGE])).toBeTruthy() }) + it('tests create consecutive branches with restored changes from stash', async () => { + const BRANCHA = `${BRANCH}-a` + const BRANCHB = `${BRANCH}-b` + + // Create tracked and untracked file changes + const changes = await createChanges() + const commitMessage = uuidv4() + const resultA = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCHA, + REMOTE_NAME, + false, + ['a'] + ) + const resultB = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCHB, + REMOTE_NAME, + false, + ['b'] + ) + await git.checkout(BRANCHA) + expect(resultA.action).toEqual('created') + expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) + expect( + await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE]) + ).toBeTruthy() + await git.checkout(BRANCHB) + expect(resultB.action).toEqual('created') + expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) + expect( + await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE]) + ).toBeTruthy() + + // Delete the local branches + await git.checkout(DEFAULT_BRANCH) + await git.exec(['branch', '--delete', '--force', BRANCHA]) + await git.exec(['branch', '--delete', '--force', BRANCHB]) + }) + // Working Base is Not Base (WBNB) it('tests no changes resulting in no new branch being created (WBNB)', async () => { @@ -2132,4 +2176,52 @@ describe('create-or-update-branch tests', () => { // The action cannot successfully create the branch expect(result.action).toEqual('none') }) + + it('tests create consecutive branches with restored changes from stash in detached HEAD state (WBNR)', async () => { + // Checkout the HEAD commit SHA + const headSha = await git.revParse('HEAD') + await git.checkout(headSha) + + const BRANCHA = `${BRANCH}-a` + const BRANCHB = `${BRANCH}-b` + + // Create tracked and untracked file changes + const changes = await createChanges() + const commitMessage = uuidv4() + const resultA = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCHA, + REMOTE_NAME, + false, + ['a'] + ) + const resultB = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCHB, + REMOTE_NAME, + false, + ['b'] + ) + await git.checkout(BRANCHA) + expect(resultA.action).toEqual('created') + expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) + expect( + await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE]) + ).toBeTruthy() + await git.checkout(BRANCHB) + expect(resultB.action).toEqual('created') + expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) + expect( + await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE]) + ).toBeTruthy() + + // Delete the local branches + await git.checkout(DEFAULT_BRANCH) + await git.exec(['branch', '--delete', '--force', BRANCHA]) + await git.exec(['branch', '--delete', '--force', BRANCHB]) + }) }) diff --git a/dist/index.js b/dist/index.js index 0aadd2c..c6d6fd1 100644 --- a/dist/index.js +++ b/dist/index.js @@ -158,9 +158,8 @@ function createOrUpdateBranch(git, commitMessage, base, branch, branchRemoteName throw new Error(`Unexpected error: ${commitResult.stderr}`); } } - // Remove uncommitted tracked and untracked changes - yield git.exec(['reset', '--hard']); - yield git.exec(['clean', '-f', '-d']); + // Stash any uncommitted tracked and untracked changes + const stashed = yield git.stashPush(['--include-untracked']); // Perform fetch and reset the working base // Commits made during the workflow will be removed if (workingBaseType == WorkingBaseType.Branch) { @@ -260,6 +259,10 @@ function createOrUpdateBranch(git, commitMessage, base, branch, branchRemoteName yield git.exec(['branch', '--delete', '--force', tempBranch]); // Checkout the working base to leave the local repository as it was found yield git.checkout(workingBase); + // Restore any stashed changes + if (stashed) { + yield git.stashPop(); + } return result; }); } @@ -844,6 +847,25 @@ class GitCommandManager { return output.stdout.trim(); }); } + stashPush(options) { + return __awaiter(this, void 0, void 0, function* () { + const args = ['stash', 'push']; + if (options) { + args.push(...options); + } + const output = yield this.exec(args); + return output.stdout.trim() !== 'No local changes to save'; + }); + } + stashPop(options) { + return __awaiter(this, void 0, void 0, function* () { + const args = ['stash', 'pop']; + if (options) { + args.push(...options); + } + yield this.exec(args); + }); + } status(options) { return __awaiter(this, void 0, void 0, function* () { const args = ['status']; diff --git a/src/create-or-update-branch.ts b/src/create-or-update-branch.ts index b687356..cb1fcbb 100644 --- a/src/create-or-update-branch.ts +++ b/src/create-or-update-branch.ts @@ -170,9 +170,8 @@ export async function createOrUpdateBranch( } } - // Remove uncommitted tracked and untracked changes - await git.exec(['reset', '--hard']) - await git.exec(['clean', '-f', '-d']) + // Stash any uncommitted tracked and untracked changes + const stashed = await git.stashPush(['--include-untracked']) // Perform fetch and reset the working base // Commits made during the workflow will be removed @@ -293,5 +292,10 @@ export async function createOrUpdateBranch( // Checkout the working base to leave the local repository as it was found await git.checkout(workingBase) + // Restore any stashed changes + if (stashed) { + await git.stashPop() + } + return result } diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts index 87a6dd9..5ab6972 100644 --- a/src/git-command-manager.ts +++ b/src/git-command-manager.ts @@ -210,6 +210,23 @@ export class GitCommandManager { return output.stdout.trim() } + async stashPush(options?: string[]): Promise { + const args = ['stash', 'push'] + if (options) { + args.push(...options) + } + const output = await this.exec(args) + return output.stdout.trim() !== 'No local changes to save' + } + + async stashPop(options?: string[]): Promise { + const args = ['stash', 'pop'] + if (options) { + args.push(...options) + } + await this.exec(args) + } + async status(options?: string[]): Promise { const args = ['status'] if (options) {