Skip to content

Commit 289eecd

Browse files
authored
Background - store base branch protection (#4364)
1 parent 2f2c135 commit 289eecd

File tree

9 files changed

+47
-6
lines changed

9 files changed

+47
-6
lines changed

src/extension/chatSessions/common/chatSessionWorktreeService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ interface ChatSessionWorktreePropertiesV1 extends ChatSessionWorktreeBasePropert
3838
interface ChatSessionWorktreePropertiesV2 extends ChatSessionWorktreeBaseProperties {
3939
readonly version: 2;
4040
readonly baseBranchName: string;
41+
readonly baseBranchProtected?: boolean;
4142
readonly pullRequestUrl?: string;
4243
}
4344

src/extension/chatSessions/vscode-node/chatSessionWorktreeServiceImpl.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ export class ChatSessionWorktreeService extends Disposable implements IChatSessi
7171
const worktreePath = await this.gitService.createWorktree(activeRepository.rootUri, { branch, commitish: baseBranch });
7272

7373
if (worktreePath && activeRepository.headCommitHash && activeRepository.headBranchName) {
74+
const baseBranchName = baseBranch ?? activeRepository.headBranchName;
75+
const baseBranchProtected = await this.gitService.isBranchProtected(activeRepository.rootUri, baseBranchName);
76+
7477
let baseCommit: string | undefined = undefined;
7578
if (baseBranch) {
7679
const refs = await this.gitService.getRefs(activeRepository.rootUri, { pattern: `refs/heads/${baseBranch}` });
@@ -80,7 +83,8 @@ export class ChatSessionWorktreeService extends Disposable implements IChatSessi
8083
return {
8184
branchName: branch,
8285
baseCommit: baseCommit ?? activeRepository.headCommitHash,
83-
baseBranchName: baseBranch ?? activeRepository.headBranchName,
86+
baseBranchName,
87+
baseBranchProtected,
8488
repositoryPath: activeRepository.rootUri.fsPath,
8589
worktreePath,
8690
version: 2

src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,16 @@ export class CopilotCLIChatSessionItemProvider extends Disposable implements vsc
270270
const metadata = worktreeProperties
271271
? {
272272
baseCommit: worktreeProperties?.baseCommit,
273+
baseBranchProtected: worktreeProperties.version === 2
274+
? worktreeProperties.baseBranchProtected === true
275+
: undefined,
273276
branchName: worktreeProperties?.branchName,
274277
isolationMode: 'worktree',
275278
repositoryPath: worktreeProperties?.repositoryPath,
276279
worktreePath: worktreeProperties?.worktreePath,
277-
pullRequestUrl: worktreeProperties.version === 2 ? worktreeProperties.pullRequestUrl : undefined,
280+
pullRequestUrl: worktreeProperties.version === 2
281+
? worktreeProperties.pullRequestUrl
282+
: undefined,
278283
} satisfies { readonly [key: string]: unknown }
279284
: {
280285
isolationMode: 'workspace',

src/extension/prompt/node/test/repoInfoTelemetry.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ suite('RepoInfoTelemetry', () => {
102102
rebase: vi.fn(),
103103
commit: vi.fn(),
104104
getRefs: vi.fn(),
105+
isBranchProtected: vi.fn(),
105106
push: vi.fn(),
106107
dispose: vi.fn()
107108
};

src/platform/git/common/gitService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Event } from '../../../util/vs/base/common/event';
1010
import { IObservable } from '../../../util/vs/base/common/observableInternal';
1111
import { equalsIgnoreCase } from '../../../util/vs/base/common/strings';
1212
import { URI } from '../../../util/vs/base/common/uri';
13-
import { Change, Commit, CommitOptions, CommitShortStat, DiffChange, LogOptions, Ref, RefQuery, RepositoryAccessDetails, RepositoryKind, Worktree } from '../vscode/git';
13+
import { Branch, Change, Commit, CommitOptions, CommitShortStat, DiffChange, LogOptions, Ref, RefQuery, RepositoryAccessDetails, RepositoryKind, Worktree } from '../vscode/git';
1414

1515
export interface RepoContext {
1616
readonly rootUri: URI;
@@ -79,6 +79,7 @@ export interface IGitService extends IDisposable {
7979
rebase(uri: URI, branch: string): Promise<void>;
8080

8181
getRefs(uri: URI, query: RefQuery, cancellationToken?: CancellationToken): Promise<Ref[]>;
82+
isBranchProtected(uri: URI, branch?: string | Branch): Promise<boolean | undefined>;
8283

8384
generateRandomBranchName(uri: URI): Promise<string | undefined>;
8485
}

src/platform/git/vscode/git.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ export interface Repository {
317317
migrateChanges(sourceRepositoryPath: string, options?: { confirmation?: boolean; deleteFromSource?: boolean; untracked?: boolean }): Promise<void>;
318318

319319
generateRandomBranchName(): Promise<string | undefined>;
320+
321+
isBranchProtected(branch?: Branch): boolean;
320322
}
321323

322324
export interface RemoteSource {

src/platform/git/vscode/gitServiceImpl.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { ILogService } from '../../log/common/logService';
2020
import { IGitExtensionService } from '../common/gitExtensionService';
2121
import { IGitService, RepoContext } from '../common/gitService';
2222
import { parseGitRemotes } from '../common/utils';
23-
import { API, APIState, Change, Commit, CommitOptions, CommitShortStat, DiffChange, LogOptions, Ref, RefQuery, Repository, RepositoryAccessDetails } from './git';
23+
import { API, APIState, Branch, Change, Commit, CommitOptions, CommitShortStat, DiffChange, LogOptions, Ref, RefQuery, Repository, RepositoryAccessDetails } from './git';
2424

2525
export class GitServiceImpl extends Disposable implements IGitService {
2626

@@ -326,6 +326,25 @@ export class GitServiceImpl extends Disposable implements IGitService {
326326
return await repository?.getRefs(query, cancellationToken) ?? [];
327327
}
328328

329+
async isBranchProtected(uri: URI, branch?: string | Branch): Promise<boolean | undefined> {
330+
try {
331+
const gitAPI = this.gitExtensionService.getExtensionApi();
332+
const repository = gitAPI?.getRepository(uri);
333+
if (!repository) {
334+
return undefined;
335+
}
336+
337+
const branchToCheck = typeof branch === 'string'
338+
? await repository.getBranch(branch)
339+
: branch;
340+
return repository.isBranchProtected(branchToCheck);
341+
} catch (error) {
342+
const branchLabel = typeof branch === 'string' ? branch : branch?.name;
343+
this.logService.error(`[GitServiceImpl][isBranchProtected] Failed to check branch protection for ${uri.toString()}${branchLabel ? ` (${branchLabel})` : ''}: ${error instanceof Error ? error.message : String(error)}`);
344+
return undefined;
345+
}
346+
}
347+
329348
async generateRandomBranchName(uri: URI): Promise<string | undefined> {
330349
try {
331350
const gitAPI = this.gitExtensionService.getExtensionApi();

src/platform/ignore/node/test/mockGitService.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { IObservable } from '../../../../util/vs/base/common/observableInternal'
1010
import { observableValue } from '../../../../util/vs/base/common/observableInternal/observables/observableValue';
1111
import { URI } from '../../../../util/vs/base/common/uri';
1212
import { IGitService, RepoContext } from '../../../git/common/gitService';
13-
import { Change, Commit, CommitOptions, CommitShortStat, DiffChange, LogOptions, Ref, RefQuery, RepositoryAccessDetails } from '../../../git/vscode/git';
13+
import { Branch, Change, Commit, CommitOptions, CommitShortStat, DiffChange, LogOptions, Ref, RefQuery, RepositoryAccessDetails } from '../../../git/vscode/git';
1414

1515
/**
1616
* A configurable mock implementation of IGitService for testing.
@@ -138,6 +138,10 @@ export class MockGitService implements IGitService {
138138
return Promise.resolve([]);
139139
}
140140

141+
isBranchProtected(_uri: URI, _branch?: string | Branch): Promise<boolean | undefined> {
142+
return Promise.resolve(undefined);
143+
}
144+
141145
generateRandomBranchName(_uri: URI): Promise<string | undefined> {
142146
return Promise.resolve(undefined);
143147
}

src/platform/test/node/simulationWorkspaceServices.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { IFileSystemService } from '../../filesystem/common/fileSystemService';
2929
import { FileType, RelativePattern } from '../../filesystem/common/fileTypes';
3030
import { NodeFileSystemService } from '../../filesystem/node/fileSystemServiceImpl';
3131
import { IGitService, RepoContext } from '../../git/common/gitService';
32-
import { Change, CommitOptions, CommitShortStat, DiffChange, Ref, RefQuery, RepositoryAccessDetails } from '../../git/vscode/git';
32+
import { Branch, Change, CommitOptions, CommitShortStat, DiffChange, Ref, RefQuery, RepositoryAccessDetails } from '../../git/vscode/git';
3333
import { AbstractLanguageDiagnosticsService } from '../../languages/common/languageDiagnosticsService';
3434
import { ILanguageFeaturesService } from '../../languages/common/languageFeaturesService';
3535
import { ILogService } from '../../log/common/logService';
@@ -818,6 +818,10 @@ export class TestingGitService implements IGitService {
818818
return [];
819819
}
820820

821+
async isBranchProtected(uri: URI, branch?: string | Branch): Promise<boolean | undefined> {
822+
return undefined;
823+
}
824+
821825
async generateRandomBranchName(_uri: URI): Promise<string | undefined> {
822826
return undefined;
823827
}

0 commit comments

Comments
 (0)