Skip to content

Commit f63c91d

Browse files
faiyaz26Copilot
andcommitted
fix: augment PATH for git subprocesses and improve explorer panel
- Fix git-lfs 'command not found' by passing an enhanced PATH to all run_git() calls, ensuring subprocesses like git-lfs filter-process can be found in /opt/homebrew/bin and other common locations - Default explorer panel to Changes tab instead of Files - Persist per-lane tab selection so switching lanes restores each lane's last chosen tab (Changes or Files) - Sort staged, unstaged, and untracked file lists alphabetically for consistent ordering across git status refreshes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 979bff2 commit f63c91d

4 files changed

Lines changed: 75 additions & 9 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/components/explorer/ChangesView.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,15 @@ export function ChangesView(props: ChangesViewProps) {
7777
return files;
7878
});
7979

80-
const stagedFiles = createMemo(() => allFiles().filter((f) => f.category === 'staged'));
81-
const unstagedFiles = createMemo(() => allFiles().filter((f) => f.category === 'unstaged'));
82-
const untrackedFiles = createMemo(() => allFiles().filter((f) => f.category === 'untracked'));
80+
const stagedFiles = createMemo(() =>
81+
allFiles().filter((f) => f.category === 'staged').sort((a, b) => a.path.localeCompare(b.path))
82+
);
83+
const unstagedFiles = createMemo(() =>
84+
allFiles().filter((f) => f.category === 'unstaged').sort((a, b) => a.path.localeCompare(b.path))
85+
);
86+
const untrackedFiles = createMemo(() =>
87+
allFiles().filter((f) => f.category === 'untracked').sort((a, b) => a.path.localeCompare(b.path))
88+
);
8389

8490
const totalChanges = createMemo(() => allFiles().length);
8591

frontend/src/components/explorer/FileExplorer.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// File Explorer - Main component for browsing project files
22

33
import { createSignal, createEffect, Show } from 'solid-js';
4+
5+
// Per-lane tab selection — persists across lane switches within the session.
6+
// Defaults to 'changes' for any lane that hasn't been visited yet.
7+
const laneTabCache = new Map<string, 'files' | 'changes'>();
48
import { useFileTree, useFileWatcher } from './hooks';
59
import { FileTree } from './FileTree';
610
import { ChangesView } from './ChangesView';
@@ -14,9 +18,21 @@ interface FileExplorerProps {
1418
}
1519

1620
export function FileExplorer(props: FileExplorerProps) {
17-
const [activeTab, setActiveTab] = createSignal<'files' | 'changes'>('files');
21+
const [activeTab, setActiveTab] = createSignal<'files' | 'changes'>(
22+
laneTabCache.get(props.laneId) ?? 'changes'
23+
);
1824
const [openFileDialogOpen, setOpenFileDialogOpen] = createSignal(false);
1925

26+
// Restore the correct tab whenever the active lane changes
27+
createEffect(() => {
28+
setActiveTab(laneTabCache.get(props.laneId) ?? 'changes');
29+
});
30+
31+
const handleTabChange = (tab: 'files' | 'changes') => {
32+
laneTabCache.set(props.laneId, tab);
33+
setActiveTab(tab);
34+
};
35+
2036
const tree = useFileTree();
2137

2238
// Reload file tree when lane or workingDir changes
@@ -47,7 +63,7 @@ export function FileExplorer(props: FileExplorerProps) {
4763
return (
4864
<div class="h-full flex flex-col bg-zed-bg-panel">
4965
{/* Tabs */}
50-
<Tabs activeTab={activeTab()} onTabChange={setActiveTab} />
66+
<Tabs activeTab={activeTab()} onTabChange={handleTabChange} />
5167

5268
{/* Content */}
5369
<div class="flex-1 overflow-auto">

src-tauri/src/git.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,55 @@ fn find_repo_root(path: &str) -> Result<String, String> {
118118
}
119119
}
120120

121+
/// Build an enhanced PATH that includes common binary locations.
122+
///
123+
/// In macOS app bundles and some Linux environments, the process PATH is minimal.
124+
/// Git subprocesses like `git-lfs filter-process` inherit this limited PATH,
125+
/// causing "command not found" errors even when the tool is installed.
126+
fn enhanced_path() -> String {
127+
let current_path = std::env::var("PATH").unwrap_or_default();
128+
129+
#[cfg(unix)]
130+
{
131+
let home = dirs::home_dir().unwrap_or_default();
132+
let home_str = home.to_string_lossy();
133+
let extra = [
134+
"/usr/local/bin",
135+
"/opt/homebrew/bin", // Homebrew on Apple Silicon
136+
"/opt/homebrew/sbin",
137+
"/usr/bin",
138+
"/bin",
139+
"/usr/sbin",
140+
"/sbin",
141+
]
142+
.iter()
143+
.map(|s| s.to_string())
144+
.chain([
145+
format!("{}/.cargo/bin", home_str),
146+
format!("{}/.local/bin", home_str),
147+
format!("{}/bin", home_str),
148+
])
149+
.collect::<Vec<_>>()
150+
.join(":");
151+
152+
if current_path.is_empty() {
153+
extra
154+
} else {
155+
format!("{}:{}", current_path, extra)
156+
}
157+
}
158+
159+
#[cfg(not(unix))]
160+
current_path
161+
}
162+
121163
/// Run a git command and return the output
122164
fn run_git(work_dir: &Path, args: &[&str]) -> Result<String, String> {
123165
let mut command = Command::new("git");
124166
command.args(args);
125167
command.current_dir(work_dir);
168+
// Augment PATH so git subprocesses (e.g. git-lfs filter-process) can be found
169+
command.env("PATH", enhanced_path());
126170

127171
let output = command
128172
.output()

0 commit comments

Comments
 (0)