Skip to content

Commit 30a8977

Browse files
Copilotalexr00
andcommitted
Add auto-stash on PR checkout feature
- Add autoStashOnCheckout setting - Implement auto-stash logic when checking out PR - Pop stash when returning to default branch Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent e7a34ad commit 30a8977

7 files changed

Lines changed: 79 additions & 0 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,11 @@
462462
"%githubPullRequests.postDone.checkoutDefaultBranchAndPull%"
463463
]
464464
},
465+
"githubPullRequests.autoStashOnCheckout": {
466+
"type": "boolean",
467+
"description": "%githubPullRequests.autoStashOnCheckout.description%",
468+
"default": false
469+
},
465470
"githubPullRequests.defaultCommentType": {
466471
"type": "string",
467472
"enum": [

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"githubPullRequests.postDone.description": "The action to take after using the 'checkout default branch' or 'delete branch' actions on a currently checked out pull request.",
8181
"githubPullRequests.postDone.checkoutDefaultBranch": "Checkout the default branch of the repository",
8282
"githubPullRequests.postDone.checkoutDefaultBranchAndPull": "Checkout the default branch of the repository and pull the latest changes",
83+
"githubPullRequests.autoStashOnCheckout.description": "Automatically stash changes when checking out a pull request, and pop the stash when checking out the default branch.",
8384
"githubPullRequests.defaultCommentType.description": "The default comment type to use when submitting a comment and there is no active review",
8485
"githubPullRequests.defaultCommentType.single": "Submits the comment as a single comment that will be immediately visible to other users",
8586
"githubPullRequests.defaultCommentType.review": "Submits the comment as a review comment that will be visible to other users once the review is submitted",

src/common/settingKeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export type PullPRBranchVariants = 'never' | 'pull' | 'pullAndMergeBase' | 'pull
3737
export const UPSTREAM_REMOTE = 'upstreamRemote';
3838
export const DEFAULT_CREATE_OPTION = 'defaultCreateOption';
3939
export const CREATE_BASE_BRANCH = 'createDefaultBaseBranch';
40+
export const AUTO_STASH_ON_CHECKOUT = 'autoStashOnCheckout';
4041

4142
export const ISSUES_SETTINGS_NAMESPACE = 'githubIssues';
4243
export const ASSIGN_WHEN_WORKING = 'assignWhenWorking';

src/github/activityBarViewProvider.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { openPullRequestOnGitHub } from '../commands';
88
import { IComment } from '../common/comment';
99
import { emojify, ensureEmojis } from '../common/emoji';
1010
import { disposeAll } from '../common/lifecycle';
11+
import Logger from '../common/logger';
1112
import { ReviewEvent } from '../common/timelineEvent';
1213
import { formatError } from '../common/utils';
1314
import { getNonce, IRequestMessage, WebviewViewBase } from '../common/webview';
@@ -130,7 +131,19 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
130131
try {
131132
const defaultBranch = await this._folderRepositoryManager.getPullRequestRepositoryDefaultBranch(this._item);
132133
const prBranch = this._folderRepositoryManager.repository.state.HEAD?.name;
134+
const shouldPopStash = this._folderRepositoryManager.stashedOnCheckout;
133135
await this._folderRepositoryManager.checkoutDefaultBranch(defaultBranch);
136+
if (shouldPopStash) {
137+
try {
138+
Logger.appendLine('Popping stash after returning to default branch', 'ActivityBarViewProvider');
139+
await vscode.commands.executeCommand('git.stashPop', this._folderRepositoryManager.repository);
140+
this._folderRepositoryManager.stashedOnCheckout = false;
141+
Logger.appendLine('Stash popped successfully', 'ActivityBarViewProvider');
142+
} catch (popError) {
143+
Logger.error(`Failed to pop stash: ${formatError(popError)}`, 'ActivityBarViewProvider');
144+
vscode.window.showWarningMessage(vscode.l10n.t('Failed to restore stashed changes: {0}', formatError(popError)));
145+
}
146+
}
134147
if (prBranch) {
135148
await this._folderRepositoryManager.cleanupAfterPullRequest(prBranch, this._item);
136149
}

src/github/folderRepositoryManager.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ export class FolderRepositoryManager extends Disposable {
209209
private _onDidChangeRepositories = this._register(new vscode.EventEmitter<{ added: boolean }>());
210210
readonly onDidChangeRepositories: vscode.Event<{ added: boolean }> = this._onDidChangeRepositories.event;
211211

212+
/**
213+
* Tracks whether changes were stashed when checking out the current PR.
214+
* Used to determine if we should pop the stash when returning to the default branch.
215+
*/
216+
private _stashedOnCheckout: boolean = false;
217+
212218
private _onDidChangeAssignableUsers = this._register(new vscode.EventEmitter<IAccount[]>());
213219
readonly onDidChangeAssignableUsers: vscode.Event<IAccount[]> = this._onDidChangeAssignableUsers.event;
214220

@@ -378,6 +384,14 @@ export class FolderRepositoryManager extends Disposable {
378384
this._onDidChangeActivePullRequest.fire({ old: oldPR, new: pullRequest });
379385
}
380386

387+
get stashedOnCheckout(): boolean {
388+
return this._stashedOnCheckout;
389+
}
390+
391+
set stashedOnCheckout(value: boolean) {
392+
this._stashedOnCheckout = value;
393+
}
394+
381395
get repository(): Repository {
382396
return this._repository;
383397
}

src/github/pullRequestOverview.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,19 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
652652
private async checkoutDefaultBranch(message: IRequestMessage<string>): Promise<void> {
653653
try {
654654
const prBranch = this._folderRepositoryManager.repository.state.HEAD?.name;
655+
const shouldPopStash = this._folderRepositoryManager.stashedOnCheckout;
655656
await this._folderRepositoryManager.checkoutDefaultBranch(message.args);
657+
if (shouldPopStash) {
658+
try {
659+
Logger.appendLine('Popping stash after returning to default branch', PullRequestOverviewPanel.ID);
660+
await vscode.commands.executeCommand('git.stashPop', this._folderRepositoryManager.repository);
661+
this._folderRepositoryManager.stashedOnCheckout = false;
662+
Logger.appendLine('Stash popped successfully', PullRequestOverviewPanel.ID);
663+
} catch (popError) {
664+
Logger.error(`Failed to pop stash: ${formatError(popError)}`, PullRequestOverviewPanel.ID);
665+
vscode.window.showWarningMessage(vscode.l10n.t('Failed to restore stashed changes: {0}', formatError(popError)));
666+
}
667+
}
656668
if (prBranch) {
657669
await this._folderRepositoryManager.cleanupAfterPullRequest(prBranch, this._item);
658670
}

src/view/reviewManager.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Disposable, disposeAll, toDisposable } from '../common/lifecycle';
1515
import Logger from '../common/logger';
1616
import { parseRepositoryRemotes, Remote } from '../common/remote';
1717
import {
18+
AUTO_STASH_ON_CHECKOUT,
1819
COMMENTS,
1920
FOCUSED_MODE,
2021
IGNORE_PR_BRANCHES,
@@ -1087,6 +1088,38 @@ export class ReviewManager extends Disposable {
10871088
this.statusBarItem.show();
10881089
this.switchingToReviewMode = true;
10891090

1091+
// Check if auto-stash is enabled and there are uncommitted changes
1092+
const autoStashEnabled = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<boolean>(AUTO_STASH_ON_CHECKOUT, false);
1093+
this._folderRepoManager.stashedOnCheckout = false;
1094+
1095+
if (autoStashEnabled) {
1096+
const workingTreeChanges = this._repository.state.workingTreeChanges;
1097+
const indexChanges = this._repository.state.indexChanges;
1098+
const hasChanges = workingTreeChanges.length > 0 || indexChanges.length > 0;
1099+
1100+
if (hasChanges) {
1101+
try {
1102+
Logger.appendLine('Auto-stashing changes before PR checkout', this.id);
1103+
// Add all tracked changes to staging area
1104+
const allChangedFiles = [
1105+
...workingTreeChanges.map(change => change.uri.fsPath),
1106+
...indexChanges.map(change => change.uri.fsPath),
1107+
];
1108+
if (allChangedFiles.length > 0) {
1109+
await this._repository.add(allChangedFiles);
1110+
}
1111+
// Stash the changes
1112+
await vscode.commands.executeCommand('git.stash', this._repository);
1113+
this._folderRepoManager.stashedOnCheckout = true;
1114+
Logger.appendLine('Changes stashed successfully', this.id);
1115+
} catch (stashError) {
1116+
Logger.error(`Failed to auto-stash changes: ${formatError(stashError)}`, this.id);
1117+
// Don't block checkout if stashing fails, but warn the user
1118+
vscode.window.showWarningMessage(vscode.l10n.t('Failed to auto-stash changes: {0}', formatError(stashError)));
1119+
}
1120+
}
1121+
}
1122+
10901123
try {
10911124
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification }, async (progress) => {
10921125
const didLocalCheckout = await this._folderRepoManager.checkoutExistingPullRequestBranch(pr, progress);

0 commit comments

Comments
 (0)