Skip to content

Commit 21a9a48

Browse files
committed
refactor: move gitDiff files functionality out of zipUtil
1 parent 98c7b96 commit 21a9a48

File tree

3 files changed

+189
-173
lines changed

3 files changed

+189
-173
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import path from 'path'
6+
import { CodeWhispererConstants } from '../../codewhisperer/indexNode'
7+
import { ChildProcess, ChildProcessOptions } from '../../shared/utilities/processUtils'
8+
import { getLogger } from '../../shared/logger/logger'
9+
import { removeAnsi } from '../../shared/utilities/textUtilities'
10+
11+
interface GitDiffOptions {
12+
projectPath: string
13+
projectName: string
14+
filepath?: string
15+
scope?: CodeWhispererConstants.CodeAnalysisScope
16+
}
17+
18+
export async function getGitDiffContent(
19+
projectPaths: string[],
20+
filepath?: string,
21+
scope?: CodeWhispererConstants.CodeAnalysisScope
22+
) {
23+
let gitDiffContent = ''
24+
for (const projectPath of projectPaths) {
25+
const projectName = path.basename(projectPath)
26+
// Get diff content
27+
gitDiffContent += await executeGitDiff({
28+
projectPath,
29+
projectName,
30+
filepath,
31+
scope,
32+
})
33+
}
34+
return gitDiffContent
35+
}
36+
37+
async function executeGitDiff(options: GitDiffOptions): Promise<string> {
38+
const { projectPath, projectName, filepath: filePath, scope } = options
39+
const isProjectScope = scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT
40+
41+
const untrackedFilesString = await getGitUntrackedFiles(projectPath)
42+
const untrackedFilesArray = untrackedFilesString?.trim()?.split('\n')?.filter(Boolean)
43+
44+
if (isProjectScope && untrackedFilesArray && !untrackedFilesArray.length) {
45+
return await generateHeadDiff(projectPath, projectName)
46+
}
47+
48+
let diffContent = ''
49+
50+
if (isProjectScope) {
51+
diffContent = await generateHeadDiff(projectPath, projectName)
52+
53+
if (untrackedFilesArray) {
54+
const untrackedDiffs = await Promise.all(
55+
untrackedFilesArray.map((file) => generateNewFileDiff(projectPath, projectName, file))
56+
)
57+
diffContent += untrackedDiffs.join('')
58+
}
59+
} else if (!isProjectScope && filePath) {
60+
const relativeFilePath = path.relative(projectPath, filePath)
61+
62+
const newFileDiff = await generateNewFileDiff(projectPath, projectName, relativeFilePath)
63+
diffContent = rewriteDiff(newFileDiff)
64+
}
65+
return diffContent
66+
}
67+
68+
async function getGitUntrackedFiles(projectPath: string): Promise<string | undefined> {
69+
const checkNewFileArgs = ['ls-files', '--others', '--exclude-standard']
70+
const checkProcess = new ChildProcess('git', checkNewFileArgs)
71+
72+
try {
73+
let output = ''
74+
await checkProcess.run({
75+
rejectOnError: true,
76+
rejectOnErrorCode: true,
77+
onStdout: (text) => {
78+
output += text
79+
},
80+
spawnOptions: {
81+
cwd: projectPath,
82+
},
83+
})
84+
return output
85+
} catch (err) {
86+
getLogger().warn(`Failed to check if file is new: ${err}`)
87+
return undefined
88+
}
89+
}
90+
91+
async function generateHeadDiff(projectPath: string, projectName: string, relativePath?: string): Promise<string> {
92+
let diffContent = ''
93+
94+
const gitArgs = [
95+
'diff',
96+
'HEAD',
97+
`--src-prefix=a/${projectName}/`,
98+
`--dst-prefix=b/${projectName}/`,
99+
...(relativePath ? [relativePath] : []),
100+
]
101+
102+
const childProcess = new ChildProcess('git', gitArgs)
103+
104+
const runOptions: ChildProcessOptions = {
105+
rejectOnError: true,
106+
rejectOnErrorCode: true,
107+
onStdout: (text) => {
108+
diffContent += text
109+
getLogger().verbose(removeAnsi(text))
110+
},
111+
onStderr: (text) => {
112+
getLogger().error(removeAnsi(text))
113+
},
114+
spawnOptions: {
115+
cwd: projectPath,
116+
},
117+
}
118+
119+
try {
120+
await childProcess.run(runOptions)
121+
return diffContent
122+
} catch (err) {
123+
getLogger().warn(`Failed to run command \`${childProcess.toString()}\`: ${err}`)
124+
return ''
125+
}
126+
}
127+
128+
async function generateNewFileDiff(projectPath: string, projectName: string, relativePath: string): Promise<string> {
129+
let diffContent = ''
130+
131+
const gitArgs = [
132+
'diff',
133+
'--no-index',
134+
`--src-prefix=a/${projectName}/`,
135+
`--dst-prefix=b/${projectName}/`,
136+
'/dev/null', // Use /dev/null as the old file
137+
relativePath,
138+
]
139+
140+
const childProcess = new ChildProcess('git', gitArgs)
141+
const runOptions: ChildProcessOptions = {
142+
rejectOnError: false,
143+
rejectOnErrorCode: false,
144+
onStdout: (text) => {
145+
diffContent += text
146+
getLogger().verbose(removeAnsi(text))
147+
},
148+
onStderr: (text) => {
149+
getLogger().error(removeAnsi(text))
150+
},
151+
spawnOptions: {
152+
cwd: projectPath,
153+
},
154+
}
155+
156+
try {
157+
await childProcess.run(runOptions)
158+
return diffContent
159+
} catch (err) {
160+
getLogger().warn(`Failed to run diff command: ${err}`)
161+
return ''
162+
}
163+
}
164+
165+
function rewriteDiff(inputStr: string): string {
166+
const lines = inputStr.split('\n')
167+
const rewrittenLines = lines.slice(0, 5).map((line) => {
168+
line = line.replace(/\\\\/g, '/')
169+
line = line.replace(/("a\/[^"]*)/g, (match, p1) => p1)
170+
line = line.replace(/("b\/[^"]*)/g, (match, p1) => p1)
171+
line = line.replace(/"/g, '')
172+
173+
return line
174+
})
175+
const outputLines = [...rewrittenLines, ...lines.slice(5)]
176+
const outputStr = outputLines.join('\n')
177+
178+
return outputStr
179+
}

packages/core/src/codewhisperer/models/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,3 +926,7 @@ export const testGenExcludePatterns = [
926926
'**/*.deb',
927927
'**/*.model',
928928
]
929+
930+
export const isFileAnalysisScope = (scope: CodeAnalysisScope) => {
931+
return scope === CodeAnalysisScope.FILE_AUTO || scope === CodeAnalysisScope.FILE_ON_DEMAND
932+
}

0 commit comments

Comments
 (0)