Path: blob/main/extensions/copilot/script/scoredEditsReconciler.ts
13383 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*--------------------------------------------------------------------------------------------*/4import { execSync } from 'child_process';5import * as fs from 'fs';6import minimist from 'minimist';7import * as path from 'path';89async function main() {10const args = minimist(process.argv.slice(2));11const filePath = args.file;12const list = args.list;13const reconcileUsingGit = args.a || args.auto;1415const filesWithMergeConflicts = await scoredEditsWithMergeConflicts();1617if (list) {18console.log(filesWithMergeConflicts.join('\n'));19return;20}2122if (filePath) {23try {24const resolvedFileContents = await resolveMergeConflictFromFile(filePath);25await fs.promises.writeFile(filePath, resolvedFileContents, 'utf8');26} catch (e: unknown) {27throw e;28}29return;30}3132if (reconcileUsingGit) {33try {34await Promise.all(filesWithMergeConflicts.map(async (filePath) => {35const resolvedFileContents = await resolveMergeConflictFromFile(filePath);36return fs.promises.writeFile(filePath, resolvedFileContents);37}));38return;39} catch (e: unknown) {40throw e;41}42}434445console.log(`46Usage: scoredEditReconciler [options]4748Options:49-a, --auto Reconcile merge conflicts automatically by finding files with merge conflicts using git50--file <path> Path to the file to resolve merge conflicts51--list List files with merge conflicts52--help Show help53`.trim());54}5556async function scoredEditsWithMergeConflicts(): Promise<string[]> /* paths */ {57const files = await findFilesWithMergeConflicts();58return files.filter(file => file.endsWith('scoredEdits.w.json'));59}6061async function findFilesWithMergeConflicts() {62try {63// Get files with merge conflicts using git command64const gitOutput = execSync('git diff --name-only --diff-filter=U').toString();6566// Split output into array of file paths67const conflictFiles = gitOutput.split('\n').filter(file => file.trim().length > 0);6869return conflictFiles.map(file => path.resolve(file));70} catch (error) {71console.error('Error finding files with merge conflicts:', error);72return [];73}74}7576async function resolveMergeConflictFromFile(filePath: string) {77const fileContents = await fs.promises.readFile(filePath, 'utf8');78return resolveMergeConflict(fileContents);79}8081export function resolveMergeConflict(fileContents: string): string {8283const headFileContents = removeNonHeadSections(fileContents);84const nonHeadFileContents = removeHeadSections(fileContents);8586const headFileAsObject = JSON.parse(headFileContents);87const nonHeadfileAsObject = JSON.parse(nonHeadFileContents);88if (JSON.stringify({ ...headFileAsObject, edits: [] }) !== JSON.stringify({ ...nonHeadfileAsObject, edits: [] })) {89throw new Error('There seems to be merge conflict outside `edits` field which this script can resolve automatically.');90}9192const mergedEdits = [...headFileAsObject.edits];9394for (const edit of nonHeadfileAsObject.edits) {95if (!mergedEdits.some(headEdit => JSON.stringify(headEdit) === JSON.stringify(edit))) {96mergedEdits.push(edit);97}98}99100const resolvedFileContents = JSON.stringify({101...headFileAsObject,102edits: mergedEdits103}, null, '\t');104105return resolvedFileContents;106}107108function removeNonHeadSections(fileContents: string) {109const lines = fileContents.split('\n');110const headLines = [];111let insideNonHead = false;112113for (const line of lines) {114if (line.startsWith('=======')) {115insideNonHead = true;116} else if (line.startsWith('>>>>>>>')) {117insideNonHead = false;118} else if (!insideNonHead && !line.startsWith('<<<<<<<')) {119headLines.push(line);120}121}122123return headLines.join('\n');124}125function removeHeadSections(fileContents: string) {126const lines = fileContents.split('\n');127const nonHeadLines = [];128let insideHead = false;129130for (const line of lines) {131if (line.startsWith('<<<<<<<')) {132insideHead = true;133} else if (line.startsWith('=======')) {134insideHead = false;135} else if (!insideHead && !line.startsWith('>>>>>>>')) {136nonHeadLines.push(line);137}138}139140return nonHeadLines.join('\n');141}142143main();144145146