优化文件树git状态检测性能
This commit is contained in:
@@ -1075,7 +1075,7 @@ pub async fn git_stash_show(
|
|||||||
pub async fn git_status(path: String) -> Result<Vec<GitStatusEntry>, AppCommandError> {
|
pub async fn git_status(path: String) -> Result<Vec<GitStatusEntry>, AppCommandError> {
|
||||||
let output = crate::process::tokio_command("git")
|
let output = crate::process::tokio_command("git")
|
||||||
.args(["-c", "core.quotePath=false"])
|
.args(["-c", "core.quotePath=false"])
|
||||||
.args(["status", "--porcelain=v1", "-uall"])
|
.args(["status", "--porcelain=v1", "-unormal"])
|
||||||
.current_dir(&path)
|
.current_dir(&path)
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -409,8 +409,10 @@ interface RenderNodeProps {
|
|||||||
gitEnabled: boolean
|
gitEnabled: boolean
|
||||||
gitStatusByPath: ReadonlyMap<string, string>
|
gitStatusByPath: ReadonlyMap<string, string>
|
||||||
gitChangedDirPaths: ReadonlySet<string>
|
gitChangedDirPaths: ReadonlySet<string>
|
||||||
|
untrackedDirPaths: ReadonlySet<string>
|
||||||
gitignoreIgnoredPaths: ReadonlySet<string>
|
gitignoreIgnoredPaths: ReadonlySet<string>
|
||||||
ancestorGitignoreIgnored: boolean
|
ancestorGitignoreIgnored: boolean
|
||||||
|
ancestorUntracked: boolean
|
||||||
onOpenFilePreview: (path: string) => void
|
onOpenFilePreview: (path: string) => void
|
||||||
onOpenFileDiff: (path: string) => void
|
onOpenFileDiff: (path: string) => void
|
||||||
onOpenDirDiff: (path: string) => void
|
onOpenDirDiff: (path: string) => void
|
||||||
@@ -433,8 +435,10 @@ function RenderNode({
|
|||||||
gitEnabled,
|
gitEnabled,
|
||||||
gitStatusByPath,
|
gitStatusByPath,
|
||||||
gitChangedDirPaths,
|
gitChangedDirPaths,
|
||||||
|
untrackedDirPaths,
|
||||||
gitignoreIgnoredPaths,
|
gitignoreIgnoredPaths,
|
||||||
ancestorGitignoreIgnored,
|
ancestorGitignoreIgnored,
|
||||||
|
ancestorUntracked,
|
||||||
onOpenFilePreview,
|
onOpenFilePreview,
|
||||||
onOpenFileDiff,
|
onOpenFileDiff,
|
||||||
onOpenDirDiff,
|
onOpenDirDiff,
|
||||||
@@ -465,7 +469,8 @@ function RenderNode({
|
|||||||
})()
|
})()
|
||||||
|
|
||||||
if (node.kind === "file") {
|
if (node.kind === "file") {
|
||||||
const gitStatusCode = gitStatusByPath.get(node.path)
|
const gitStatusCode =
|
||||||
|
gitStatusByPath.get(node.path) ?? (ancestorUntracked ? "??" : undefined)
|
||||||
const absolutePath = joinFsPath(workspacePath, node.path)
|
const absolutePath = joinFsPath(workspacePath, node.path)
|
||||||
const dirPath = parentDir(absolutePath)
|
const dirPath = parentDir(absolutePath)
|
||||||
const isGitMenuDisabled = !gitEnabled || isGitignoreIgnored
|
const isGitMenuDisabled = !gitEnabled || isGitignoreIgnored
|
||||||
@@ -599,7 +604,11 @@ function RenderNode({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const absolutePath = joinFsPath(workspacePath, node.path)
|
const absolutePath = joinFsPath(workspacePath, node.path)
|
||||||
const dirHasChanges = !isGitignoreIgnored && gitChangedDirPaths.has(node.path)
|
const isThisDirUntracked =
|
||||||
|
ancestorUntracked || untrackedDirPaths.has(node.path)
|
||||||
|
const dirHasChanges =
|
||||||
|
!isGitignoreIgnored &&
|
||||||
|
(gitChangedDirPaths.has(node.path) || isThisDirUntracked)
|
||||||
const isGitMenuDisabled = !gitEnabled || isGitignoreIgnored
|
const isGitMenuDisabled = !gitEnabled || isGitignoreIgnored
|
||||||
const shouldRenderChildren = expandedPaths.has(node.path)
|
const shouldRenderChildren = expandedPaths.has(node.path)
|
||||||
|
|
||||||
@@ -638,8 +647,10 @@ function RenderNode({
|
|||||||
gitEnabled={gitEnabled}
|
gitEnabled={gitEnabled}
|
||||||
gitStatusByPath={gitStatusByPath}
|
gitStatusByPath={gitStatusByPath}
|
||||||
gitChangedDirPaths={gitChangedDirPaths}
|
gitChangedDirPaths={gitChangedDirPaths}
|
||||||
|
untrackedDirPaths={untrackedDirPaths}
|
||||||
gitignoreIgnoredPaths={gitignoreIgnoredPaths}
|
gitignoreIgnoredPaths={gitignoreIgnoredPaths}
|
||||||
ancestorGitignoreIgnored={isGitignoreIgnored}
|
ancestorGitignoreIgnored={isGitignoreIgnored}
|
||||||
|
ancestorUntracked={isThisDirUntracked}
|
||||||
onOpenFilePreview={onOpenFilePreview}
|
onOpenFilePreview={onOpenFilePreview}
|
||||||
onOpenFileDiff={onOpenFileDiff}
|
onOpenFileDiff={onOpenFileDiff}
|
||||||
onOpenDirDiff={onOpenDirDiff}
|
onOpenDirDiff={onOpenDirDiff}
|
||||||
@@ -908,7 +919,10 @@ export function FileTreeTab() {
|
|||||||
(entries: { file: string; status: string }[]) => {
|
(entries: { file: string; status: string }[]) => {
|
||||||
const nextStatusByPath = new Map<string, string>()
|
const nextStatusByPath = new Map<string, string>()
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const normalizedPath = normalizeGitStatusPath(entry.file)
|
const raw = normalizeGitStatusPath(entry.file)
|
||||||
|
if (!raw) continue
|
||||||
|
// Strip trailing slash (directory entries from -unormal)
|
||||||
|
const normalizedPath = raw.replace(/\/+$/, "")
|
||||||
if (!normalizedPath) continue
|
if (!normalizedPath) continue
|
||||||
nextStatusByPath.set(normalizedPath, entry.status)
|
nextStatusByPath.set(normalizedPath, entry.status)
|
||||||
}
|
}
|
||||||
@@ -1182,6 +1196,20 @@ export function FileTreeTab() {
|
|||||||
return dirs
|
return dirs
|
||||||
}, [gitStatusByPath])
|
}, [gitStatusByPath])
|
||||||
|
|
||||||
|
// Directories that are entirely untracked (from git status -unormal)
|
||||||
|
const untrackedDirPaths = useMemo(() => {
|
||||||
|
const dirs = new Set<string>()
|
||||||
|
for (const [path, status] of gitStatusByPath.entries()) {
|
||||||
|
if (status.trim() === "??") {
|
||||||
|
// Check if this path is a directory in the file tree
|
||||||
|
if (dirChildrenByPath.has(path)) {
|
||||||
|
dirs.add(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirs
|
||||||
|
}, [gitStatusByPath, dirChildrenByPath])
|
||||||
|
|
||||||
const handleTreeSelect = useCallback(
|
const handleTreeSelect = useCallback(
|
||||||
(path: string) => {
|
(path: string) => {
|
||||||
if (!filePathSet.has(path)) return
|
if (!filePathSet.has(path)) return
|
||||||
@@ -2163,8 +2191,10 @@ export function FileTreeTab() {
|
|||||||
gitEnabled={gitEnabled}
|
gitEnabled={gitEnabled}
|
||||||
gitStatusByPath={gitStatusByPath}
|
gitStatusByPath={gitStatusByPath}
|
||||||
gitChangedDirPaths={gitChangedDirPaths}
|
gitChangedDirPaths={gitChangedDirPaths}
|
||||||
|
untrackedDirPaths={untrackedDirPaths}
|
||||||
gitignoreIgnoredPaths={gitignoreIgnoredPaths}
|
gitignoreIgnoredPaths={gitignoreIgnoredPaths}
|
||||||
ancestorGitignoreIgnored={false}
|
ancestorGitignoreIgnored={false}
|
||||||
|
ancestorUntracked={false}
|
||||||
onOpenFilePreview={(path) => {
|
onOpenFilePreview={(path) => {
|
||||||
void openFilePreview(path)
|
void openFilePreview(path)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ function normalizePathSegments(path: string): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeGitStatusPath(path: string): string {
|
function normalizeGitStatusPath(path: string): string {
|
||||||
const normalized = path.trim()
|
const normalized = path.trim().replace(/\/+$/, "")
|
||||||
const renameSeparator = " -> "
|
const renameSeparator = " -> "
|
||||||
const renameIndex = normalized.lastIndexOf(renameSeparator)
|
const renameIndex = normalized.lastIndexOf(renameSeparator)
|
||||||
if (renameIndex < 0) return normalized
|
if (renameIndex < 0) return normalized
|
||||||
|
|||||||
Reference in New Issue
Block a user