Path: blob/main/extensions/copilot/src/platform/git/vscode-node/utils.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as path from 'path';6import { Uri } from 'vscode';7import { Change, DiffChange } from '../vscode/git';8import { RepoContext } from '../common/gitService';9import { coalesce } from '../../../util/vs/base/common/arrays';10import { ResourceSet } from '../../../util/vs/base/common/map';11import { isEqual, relativePath } from '../../../util/vs/base/common/resources';1213export function parseGitChangesRaw(repositoryRoot: string, raw: string): DiffChange[] {14const changes: Change[] = [];15const numStats = new Map<string, { insertions: number; deletions: number }>();1617let index = 0;18const segments = raw.trim().split('\x00').filter(s => s);1920segmentsLoop:21while (index < segments.length) {22const segment = segments[index++];23if (!segment) {24break;25}2627if (segment.startsWith(':')) {28// Parse --raw output29const [, , , , change] = segment.split(' ');30const filePath = segments[index++];31const originalUri = Uri.file(path.isAbsolute(filePath) ? filePath : path.join(repositoryRoot, filePath));3233let uri = originalUri;34let renameUri = originalUri;35let status = 7; /* Status.UNTRACKED */3637switch (change[0]) {38case 'A':39status = 1; /* Status.INDEX_ADDED */40break;41case 'M':42status = 5; /* Status.MODIFIED */43break;44case 'D':45status = 6; /* Status.DELETED */46break;47case 'R': {48if (index >= segments.length) {49break;50}51const newPath = segments[index++];52if (!newPath) {53break;54}5556status = 3; /* Status.INDEX_RENAMED */57uri = renameUri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(repositoryRoot, newPath));58break;59}60default:61// Unknown status62break segmentsLoop;63}6465changes.push({ status, uri, originalUri, renameUri });66} else {67// Parse --numstat output68const [insertions, deletions, filePath] = segment.split('\t');6970let numstatPath: string;71if (filePath === '') {72// For renamed files, filePath is empty and the old/new paths73// are in the next two null-terminated segments. We skip the74// old path and use the new path for the stats key.75index++;7677const renamePath = segments[index++];78numstatPath = path.isAbsolute(renamePath) ? renamePath : path.join(repositoryRoot, renamePath);79} else {80numstatPath = path.isAbsolute(filePath) ? filePath : path.join(repositoryRoot, filePath);81}8283numStats.set(numstatPath, {84insertions: insertions === '-' ? 0 : parseInt(insertions),85deletions: deletions === '-' ? 0 : parseInt(deletions),86});87}88}8990return changes.map(change => ({91...change,92insertions: numStats.get(change.uri.fsPath)?.insertions ?? 0,93deletions: numStats.get(change.uri.fsPath)?.deletions ?? 0,94}));95}9697export function getUncommittedFilePaths(repository: RepoContext): string[] {98const resources = new ResourceSet();99100const allChanges = [101...repository.changes?.indexChanges ?? [],102...repository.changes?.workingTree ?? [],103...repository.changes?.untrackedChanges ?? []104];105106for (const change of allChanges) {107resources.add(change.uri);108if (109change.originalUri &&110!isEqual(change.uri, change.originalUri)111) {112resources.add(change.originalUri);113}114}115116const relativePaths = coalesce(Array.from(resources)117.map(uri => relativePath(repository.rootUri, uri)));118119// Git expects forward slashes even on Windows120return relativePaths.map(p => p.replace(/\\/g, '/'));121}122123export function buildTempIndexEnv(repository: RepoContext, indexFile: string): Record<string, string> {124if (!repository.isUsingVirtualFileSystem) {125return { GIT_INDEX_FILE: indexFile };126}127128// In GVFS repos, the command hook acquires a lock that blocks file writes while129// any git command runs. By setting COMMAND_HOOK_LOCK, temp index operations (ex:130// add, read-tree, write-tree, diff --cached) won't hold the main lock.131return {132COMMAND_HOOK_LOCK: '1',133GIT_INDEX_FILE: indexFile134};135}136137138