Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { promises as fs } from 'fs';
import * as vscode from 'vscode';
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
import { getGitHubRepoInfoFromContext, IGitService } from '../../../platform/git/common/gitService';
import { buildTempIndexEnv, parseGitChangesRaw } from '../../../platform/git/vscode-node/utils';
import { buildTempIndexEnv, getUncommittedFilePaths, parseGitChangesRaw } from '../../../platform/git/vscode-node/utils';
import { DiffChange } from '../../../platform/git/vscode/git';
import { ILogService } from '../../../platform/log/common/logService';
import { SequencerByKey } from '../../../util/vs/base/common/async';
Expand Down Expand Up @@ -197,6 +197,8 @@ export class ChatSessionWorkspaceFolderService extends Disposable implements ICh
// Tracked + untracked changes
const tmpDirName = `vscode-sessions-${generateUuid()}`;
const diffIndexFile = path.join(this.extensionContext.globalStorageUri.fsPath, tmpDirName, 'diff.index');
const pathspecFile = path.join(this.extensionContext.globalStorageUri.fsPath, tmpDirName, `pathspec.txt`);

const env = buildTempIndexEnv(repository, diffIndexFile);

try {
Expand All @@ -212,7 +214,9 @@ export class ChatSessionWorkspaceFolderService extends Disposable implements ICh
}

// Stage entire working directory into temp index
await this.gitService.exec(repository.rootUri, ['add', '-A', '--', '.'], env);
const uncommittedFilePaths = getUncommittedFilePaths(repository);
await fs.writeFile(pathspecFile, uncommittedFilePaths.join('\n'), 'utf8');
await this.gitService.exec(repository.rootUri, ['add', '-A', `--pathspec-from-file=${pathspecFile}`], env);
Comment thread
lszomoru marked this conversation as resolved.

// Diff the temp index with the base branch
const result = await this.gitService.exec(repository.rootUri, ['diff', '--cached', '--raw', '--numstat', '--diff-filter=ADMR', ...noRenamesArg, '-z', ...mergeBaseArg, '--'], env);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { IChatSessionMetadataStore } from '../common/chatSessionMetadataStore';
import { IChatSessionWorkspaceFolderService } from '../common/chatSessionWorkspaceFolderService';
import { IChatSessionWorktreeCheckpointService } from '../common/chatSessionWorktreeCheckpointService';
import { IChatSessionWorktreeService } from '../common/chatSessionWorktreeService';
import { buildTempIndexEnv } from '../../../platform/git/vscode-node/utils';
import { buildTempIndexEnv, getUncommittedFilePaths } from '../../../platform/git/vscode-node/utils';

const CHECKPOINT_REF_PREFIX = 'refs/sessions/';

Expand Down Expand Up @@ -207,28 +207,33 @@ export class ChatSessionWorktreeCheckpointService extends Disposable implements

private async _createCheckpoint(sessionId: string, repository: RepoContext, turnNumber: number, parentCheckpointRef?: string): Promise<string | undefined> {
const repositoryUri = repository.rootUri;

const tmpDirName = `vscode-sessions-${sessionId}-${generateUuid()}`;
const checkpointIndexFile = path.join(this.extensionContext.globalStorageUri.fsPath, tmpDirName, `checkpoint.index`);
const pathspecFile = path.join(this.extensionContext.globalStorageUri.fsPath, tmpDirName, `pathspec.txt`);

const env = buildTempIndexEnv(repository, checkpointIndexFile);

try {
// Create temp index file directory
await fs.mkdir(path.dirname(checkpointIndexFile), { recursive: true });

// Resolve parent checkpoint ref
const parentCommitOid = parentCheckpointRef
? await this.gitService.exec(repositoryUri, ['rev-parse', parentCheckpointRef])
: undefined;

// Populate temp index from previous checkpoint tree (or HEAD for the baseline)
await this.gitService.exec(repositoryUri, ['read-tree', parentCommitOid ?? 'HEAD'], env);
// Populate temp index from HEAD
await this.gitService.exec(repositoryUri, ['read-tree', 'HEAD'], env);

// Stage entire working directory into temp index
await this.gitService.exec(repositoryUri, ['add', '-A', '--', '.'], env);
const uncommittedFilePaths = getUncommittedFilePaths(repository);
await fs.writeFile(pathspecFile, uncommittedFilePaths.join('\n'), 'utf8');
await this.gitService.exec(repositoryUri, ['add', '-A', `--pathspec-from-file=${pathspecFile}`], env);
Comment thread
lszomoru marked this conversation as resolved.

// Write the temp index as a tree object
const treeOid = await this.gitService.exec(repositoryUri, ['write-tree'], env);

// Resolve parent checkpoint ref
const parentCommitOid = parentCheckpointRef
? await this.gitService.exec(repositoryUri, ['rev-parse', parentCheckpointRef])
: undefined;

// Create a commit pointing to the tree, chained to the previous checkpoint
const commitTreeArgs = ['commit-tree', treeOid, ...(parentCommitOid ? ['-p', parentCommitOid] : []), '-m', `Session ${sessionId} - checkpoint turn ${turnNumber}`];
const commitOid = await this.gitService.exec(repositoryUri, commitTreeArgs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IVSCodeExtensionContext } from '../../../platform/extContext/common/ext
import { IGitCommitMessageService } from '../../../platform/git/common/gitCommitMessageService';
import { getGitHubRepoInfoFromContext, IGitService, RepoContext } from '../../../platform/git/common/gitService';
import { toGitUri } from '../../../platform/git/common/utils';
import { buildTempIndexEnv, parseGitChangesRaw } from '../../../platform/git/vscode-node/utils';
import { buildTempIndexEnv, getUncommittedFilePaths, parseGitChangesRaw } from '../../../platform/git/vscode-node/utils';
import { DiffChange } from '../../../platform/git/vscode/git';
import { ILogService } from '../../../platform/log/common/logService';
import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
Expand Down Expand Up @@ -729,6 +729,8 @@ export class ChatSessionWorktreeService extends Disposable implements IChatSessi
// Tracked + untracked changes
const tmpDirName = `vscode-sessions-${sessionId}-${generateUuid()}`;
const diffIndexFile = path.join(this.extensionContext.globalStorageUri.fsPath, tmpDirName, 'diff.index');
const pathspecFile = path.join(this.extensionContext.globalStorageUri.fsPath, tmpDirName, `pathspec.txt`);

const env = buildTempIndexEnv(worktreeRepository, diffIndexFile);

try {
Expand All @@ -739,7 +741,9 @@ export class ChatSessionWorktreeService extends Disposable implements IChatSessi
await this.gitService.exec(worktreePath, ['read-tree', 'HEAD'], env);

// Stage entire working directory into temp index
await this.gitService.exec(worktreePath, ['add', '-A', '--', '.'], env);
const uncommittedFilePaths = getUncommittedFilePaths(worktreeRepository);
await fs.writeFile(pathspecFile, uncommittedFilePaths.join('\n'), 'utf8');
await this.gitService.exec(worktreePath, ['add', '-A', `--pathspec-from-file=${pathspecFile}`], env);

// Diff the temp index with the base branch
const result = await this.gitService.exec(worktreePath, ['diff', '--cached', '--raw', '--numstat', '--diff-filter=ADMR', ...noRenamesArg, '-z', '--merge-base', worktreeProperties.baseBranchName, '--'], env);
Expand Down
29 changes: 29 additions & 0 deletions extensions/copilot/src/platform/git/vscode-node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import * as path from 'path';
import { Uri } from 'vscode';
import { Change, DiffChange } from '../vscode/git';
import { RepoContext } from '../common/gitService';
import { coalesce } from '../../../util/vs/base/common/arrays';
import { ResourceSet } from '../../../util/vs/base/common/map';
import { isEqual, relativePath } from '../../../util/vs/base/common/resources';

export function parseGitChangesRaw(repositoryRoot: string, raw: string): DiffChange[] {
const changes: Change[] = [];
Expand Down Expand Up @@ -92,6 +95,32 @@ export function parseGitChangesRaw(repositoryRoot: string, raw: string): DiffCha
}));
}

export function getUncommittedFilePaths(repository: RepoContext): string[] {
const resources = new ResourceSet();

const allChanges = [
...repository.changes?.indexChanges ?? [],
...repository.changes?.workingTree ?? [],
...repository.changes?.untrackedChanges ?? []
Comment thread
lszomoru marked this conversation as resolved.
];

for (const change of allChanges) {
resources.add(change.uri);
if (
change.originalUri &&
!isEqual(change.uri, change.originalUri)
) {
resources.add(change.originalUri);
}
}

const relativePaths = coalesce(Array.from(resources)
.map(uri => relativePath(repository.rootUri, uri)));

// Git expects forward slashes even on Windows
return relativePaths.map(p => p.replace(/\\/g, '/'));
}

export function buildTempIndexEnv(repository: RepoContext, indexFile: string): Record<string, string> {
if (!repository.isUsingVirtualFileSystem) {
return { GIT_INDEX_FILE: indexFile };
Expand Down
Loading