Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/git/vscode-node/utils.ts
13401 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as path from 'path';
7
import { Uri } from 'vscode';
8
import { Change, DiffChange } from '../vscode/git';
9
import { RepoContext } from '../common/gitService';
10
import { coalesce } from '../../../util/vs/base/common/arrays';
11
import { ResourceSet } from '../../../util/vs/base/common/map';
12
import { isEqual, relativePath } from '../../../util/vs/base/common/resources';
13
14
export function parseGitChangesRaw(repositoryRoot: string, raw: string): DiffChange[] {
15
const changes: Change[] = [];
16
const numStats = new Map<string, { insertions: number; deletions: number }>();
17
18
let index = 0;
19
const segments = raw.trim().split('\x00').filter(s => s);
20
21
segmentsLoop:
22
while (index < segments.length) {
23
const segment = segments[index++];
24
if (!segment) {
25
break;
26
}
27
28
if (segment.startsWith(':')) {
29
// Parse --raw output
30
const [, , , , change] = segment.split(' ');
31
const filePath = segments[index++];
32
const originalUri = Uri.file(path.isAbsolute(filePath) ? filePath : path.join(repositoryRoot, filePath));
33
34
let uri = originalUri;
35
let renameUri = originalUri;
36
let status = 7; /* Status.UNTRACKED */
37
38
switch (change[0]) {
39
case 'A':
40
status = 1; /* Status.INDEX_ADDED */
41
break;
42
case 'M':
43
status = 5; /* Status.MODIFIED */
44
break;
45
case 'D':
46
status = 6; /* Status.DELETED */
47
break;
48
case 'R': {
49
if (index >= segments.length) {
50
break;
51
}
52
const newPath = segments[index++];
53
if (!newPath) {
54
break;
55
}
56
57
status = 3; /* Status.INDEX_RENAMED */
58
uri = renameUri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(repositoryRoot, newPath));
59
break;
60
}
61
default:
62
// Unknown status
63
break segmentsLoop;
64
}
65
66
changes.push({ status, uri, originalUri, renameUri });
67
} else {
68
// Parse --numstat output
69
const [insertions, deletions, filePath] = segment.split('\t');
70
71
let numstatPath: string;
72
if (filePath === '') {
73
// For renamed files, filePath is empty and the old/new paths
74
// are in the next two null-terminated segments. We skip the
75
// old path and use the new path for the stats key.
76
index++;
77
78
const renamePath = segments[index++];
79
numstatPath = path.isAbsolute(renamePath) ? renamePath : path.join(repositoryRoot, renamePath);
80
} else {
81
numstatPath = path.isAbsolute(filePath) ? filePath : path.join(repositoryRoot, filePath);
82
}
83
84
numStats.set(numstatPath, {
85
insertions: insertions === '-' ? 0 : parseInt(insertions),
86
deletions: deletions === '-' ? 0 : parseInt(deletions),
87
});
88
}
89
}
90
91
return changes.map(change => ({
92
...change,
93
insertions: numStats.get(change.uri.fsPath)?.insertions ?? 0,
94
deletions: numStats.get(change.uri.fsPath)?.deletions ?? 0,
95
}));
96
}
97
98
export function getUncommittedFilePaths(repository: RepoContext): string[] {
99
const resources = new ResourceSet();
100
101
const allChanges = [
102
...repository.changes?.indexChanges ?? [],
103
...repository.changes?.workingTree ?? [],
104
...repository.changes?.untrackedChanges ?? []
105
];
106
107
for (const change of allChanges) {
108
resources.add(change.uri);
109
if (
110
change.originalUri &&
111
!isEqual(change.uri, change.originalUri)
112
) {
113
resources.add(change.originalUri);
114
}
115
}
116
117
const relativePaths = coalesce(Array.from(resources)
118
.map(uri => relativePath(repository.rootUri, uri)));
119
120
// Git expects forward slashes even on Windows
121
return relativePaths.map(p => p.replace(/\\/g, '/'));
122
}
123
124
export function buildTempIndexEnv(repository: RepoContext, indexFile: string): Record<string, string> {
125
if (!repository.isUsingVirtualFileSystem) {
126
return { GIT_INDEX_FILE: indexFile };
127
}
128
129
// In GVFS repos, the command hook acquires a lock that blocks file writes while
130
// any git command runs. By setting COMMAND_HOOK_LOCK, temp index operations (ex:
131
// add, read-tree, write-tree, diff --cached) won't hold the main lock.
132
return {
133
COMMAND_HOOK_LOCK: '1',
134
GIT_INDEX_FILE: indexFile
135
};
136
}
137
138