Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/script/scoredEditsReconciler.ts
13383 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
import { execSync } from 'child_process';
6
import * as fs from 'fs';
7
import minimist from 'minimist';
8
import * as path from 'path';
9
10
async function main() {
11
const args = minimist(process.argv.slice(2));
12
const filePath = args.file;
13
const list = args.list;
14
const reconcileUsingGit = args.a || args.auto;
15
16
const filesWithMergeConflicts = await scoredEditsWithMergeConflicts();
17
18
if (list) {
19
console.log(filesWithMergeConflicts.join('\n'));
20
return;
21
}
22
23
if (filePath) {
24
try {
25
const resolvedFileContents = await resolveMergeConflictFromFile(filePath);
26
await fs.promises.writeFile(filePath, resolvedFileContents, 'utf8');
27
} catch (e: unknown) {
28
throw e;
29
}
30
return;
31
}
32
33
if (reconcileUsingGit) {
34
try {
35
await Promise.all(filesWithMergeConflicts.map(async (filePath) => {
36
const resolvedFileContents = await resolveMergeConflictFromFile(filePath);
37
return fs.promises.writeFile(filePath, resolvedFileContents);
38
}));
39
return;
40
} catch (e: unknown) {
41
throw e;
42
}
43
}
44
45
46
console.log(`
47
Usage: scoredEditReconciler [options]
48
49
Options:
50
-a, --auto Reconcile merge conflicts automatically by finding files with merge conflicts using git
51
--file <path> Path to the file to resolve merge conflicts
52
--list List files with merge conflicts
53
--help Show help
54
`.trim());
55
}
56
57
async function scoredEditsWithMergeConflicts(): Promise<string[]> /* paths */ {
58
const files = await findFilesWithMergeConflicts();
59
return files.filter(file => file.endsWith('scoredEdits.w.json'));
60
}
61
62
async function findFilesWithMergeConflicts() {
63
try {
64
// Get files with merge conflicts using git command
65
const gitOutput = execSync('git diff --name-only --diff-filter=U').toString();
66
67
// Split output into array of file paths
68
const conflictFiles = gitOutput.split('\n').filter(file => file.trim().length > 0);
69
70
return conflictFiles.map(file => path.resolve(file));
71
} catch (error) {
72
console.error('Error finding files with merge conflicts:', error);
73
return [];
74
}
75
}
76
77
async function resolveMergeConflictFromFile(filePath: string) {
78
const fileContents = await fs.promises.readFile(filePath, 'utf8');
79
return resolveMergeConflict(fileContents);
80
}
81
82
export function resolveMergeConflict(fileContents: string): string {
83
84
const headFileContents = removeNonHeadSections(fileContents);
85
const nonHeadFileContents = removeHeadSections(fileContents);
86
87
const headFileAsObject = JSON.parse(headFileContents);
88
const nonHeadfileAsObject = JSON.parse(nonHeadFileContents);
89
if (JSON.stringify({ ...headFileAsObject, edits: [] }) !== JSON.stringify({ ...nonHeadfileAsObject, edits: [] })) {
90
throw new Error('There seems to be merge conflict outside `edits` field which this script can resolve automatically.');
91
}
92
93
const mergedEdits = [...headFileAsObject.edits];
94
95
for (const edit of nonHeadfileAsObject.edits) {
96
if (!mergedEdits.some(headEdit => JSON.stringify(headEdit) === JSON.stringify(edit))) {
97
mergedEdits.push(edit);
98
}
99
}
100
101
const resolvedFileContents = JSON.stringify({
102
...headFileAsObject,
103
edits: mergedEdits
104
}, null, '\t');
105
106
return resolvedFileContents;
107
}
108
109
function removeNonHeadSections(fileContents: string) {
110
const lines = fileContents.split('\n');
111
const headLines = [];
112
let insideNonHead = false;
113
114
for (const line of lines) {
115
if (line.startsWith('=======')) {
116
insideNonHead = true;
117
} else if (line.startsWith('>>>>>>>')) {
118
insideNonHead = false;
119
} else if (!insideNonHead && !line.startsWith('<<<<<<<')) {
120
headLines.push(line);
121
}
122
}
123
124
return headLines.join('\n');
125
}
126
function removeHeadSections(fileContents: string) {
127
const lines = fileContents.split('\n');
128
const nonHeadLines = [];
129
let insideHead = false;
130
131
for (const line of lines) {
132
if (line.startsWith('<<<<<<<')) {
133
insideHead = true;
134
} else if (line.startsWith('=======')) {
135
insideHead = false;
136
} else if (!insideHead && !line.startsWith('>>>>>>>')) {
137
nonHeadLines.push(line);
138
}
139
}
140
141
return nonHeadLines.join('\n');
142
}
143
144
main();
145
146