Skip to content

Commit e05683b

Browse files
committed
Fix git status path handling
1 parent 74b9070 commit e05683b

2 files changed

Lines changed: 85 additions & 50 deletions

File tree

anycode-backend/src/git.rs

Lines changed: 78 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
use std::path::{Path, PathBuf};
2-
use serde::{Deserialize, Serialize};
3-
use serde_json::{json, Value};
4-
use git2::{Repository, Status, StatusOptions};
51
use anyhow::{Context, Result};
2+
use git2::{Repository, Status, StatusOptions};
3+
use serde::{Deserialize, Serialize};
4+
use serde_json::{Value, json};
5+
use std::path::{Path, PathBuf};
66
use tracing::info;
77

88
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
99
#[serde(rename_all = "lowercase")]
1010
pub enum FileStatus {
11-
Modified, Added, Deleted, Renamed, Conflict,
11+
Modified,
12+
Added,
13+
Deleted,
14+
Renamed,
15+
Conflict,
1216
}
1317

1418
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
@@ -106,8 +110,10 @@ impl GitManager {
106110
/// Get current git status
107111
pub fn status(&self) -> Result<GitStatus> {
108112
let repo = self.repo()?;
113+
let repo_root = repo.workdir().unwrap_or(Path::new("."));
109114

110-
let branch = repo.head()
115+
let branch = repo
116+
.head()
111117
.map(|h| h.shorthand().unwrap_or("HEAD").to_string())
112118
.unwrap_or_else(|_| "HEAD".to_string());
113119

@@ -121,25 +127,38 @@ impl GitManager {
121127
let mut files: Vec<GitFileStatus> = Vec::new();
122128

123129
for entry in statuses.iter() {
124-
let path = entry.path().unwrap_or("").to_string();
130+
let relative_path = entry.path().unwrap_or("");
125131
let status = entry.status();
126132

127-
let file_status = if status.contains(Status::WT_NEW) || status.contains(Status::INDEX_NEW) {
133+
let file_status = if status.contains(Status::WT_NEW)
134+
|| status.contains(Status::INDEX_NEW)
135+
{
128136
FileStatus::Added
129-
} else if status.contains(Status::WT_DELETED) || status.contains(Status::INDEX_DELETED) {
137+
} else if status.contains(Status::WT_DELETED) || status.contains(Status::INDEX_DELETED)
138+
{
130139
FileStatus::Deleted
131-
} else if status.contains(Status::WT_MODIFIED) || status.contains(Status::INDEX_MODIFIED) {
140+
} else if status.contains(Status::WT_MODIFIED)
141+
|| status.contains(Status::INDEX_MODIFIED)
142+
{
132143
FileStatus::Modified
133-
} else if status.contains(Status::WT_RENAMED) || status.contains(Status::INDEX_RENAMED) {
144+
} else if status.contains(Status::WT_RENAMED) || status.contains(Status::INDEX_RENAMED)
145+
{
134146
FileStatus::Renamed
135147
} else {
136148
continue;
137149
};
138150

139-
files.push(GitFileStatus { path, status: file_status });
151+
files.push(GitFileStatus {
152+
path: repo_root.join(relative_path).to_string_lossy().to_string(),
153+
status: file_status,
154+
});
140155
}
141156

142-
info!("Git status: {} files changed on branch {}", files.len(), branch);
157+
info!(
158+
"Git status: {} files changed on branch {}",
159+
files.len(),
160+
branch
161+
);
143162

144163
Ok(GitStatus { files, branch })
145164
}
@@ -152,7 +171,11 @@ impl GitManager {
152171
};
153172

154173
if self.status_cache != new_status {
155-
info!("Git status changed: {} files on branch {}", new_status.files.len(), new_status.branch);
174+
info!(
175+
"Git status changed: {} files on branch {}",
176+
new_status.files.len(),
177+
new_status.branch
178+
);
156179
self.status_cache = new_status.clone();
157180
Some(new_status)
158181
} else {
@@ -172,7 +195,8 @@ impl GitManager {
172195
let file_path = Path::new(path);
173196

174197
let relative_path = if file_path.is_absolute() {
175-
file_path.strip_prefix(repo_path)
198+
file_path
199+
.strip_prefix(repo_path)
176200
.map(|p| p.to_string_lossy().to_string())
177201
.unwrap_or_else(|_| path.to_string())
178202
} else {
@@ -183,15 +207,25 @@ impl GitManager {
183207
let entry = match tree.get_path(Path::new(&relative_path)) {
184208
Ok(e) => e,
185209
Err(_) => {
186-
return Ok(FileOriginal { content: String::new(), is_new: true });
210+
return Ok(FileOriginal {
211+
content: String::new(),
212+
is_new: true,
213+
});
187214
}
188215
};
189216

190217
let blob = repo.find_blob(entry.id())?;
191218
let content = std::str::from_utf8(blob.content())?.to_string();
192219

193-
info!("Got original content for {}: {} bytes", relative_path, content.len());
194-
Ok(FileOriginal { content, is_new: false })
220+
info!(
221+
"Got original content for {}: {} bytes",
222+
relative_path,
223+
content.len()
224+
);
225+
Ok(FileOriginal {
226+
content,
227+
is_new: false,
228+
})
195229
}
196230

197231
/// Commit files
@@ -221,11 +255,12 @@ impl GitManager {
221255
let tree_id = index.write_tree()?;
222256
let tree = repo.find_tree(tree_id)?;
223257

224-
let sig = repo.signature().or_else(|_| {
225-
git2::Signature::now("Anycode User", "user@anycode.dev")
226-
})?;
258+
let sig = repo
259+
.signature()
260+
.or_else(|_| git2::Signature::now("Anycode User", "user@anycode.dev"))?;
227261

228-
let parents: Vec<git2::Commit> = repo.head()
262+
let parents: Vec<git2::Commit> = repo
263+
.head()
229264
.ok()
230265
.and_then(|h| h.peel_to_commit().ok())
231266
.map(|c| vec![c])
@@ -246,8 +281,7 @@ impl GitManager {
246281
let mut remote = repo.find_remote("origin")?;
247282
let head = repo.head()?;
248283

249-
let branch_name = head.shorthand()
250-
.context("Detached HEAD state")?;
284+
let branch_name = head.shorthand().context("Detached HEAD state")?;
251285

252286
let refspec = format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name);
253287

@@ -270,8 +304,7 @@ impl GitManager {
270304
let repo = self.repo()?;
271305
let mut remote = repo.find_remote("origin")?;
272306
let head = repo.head()?;
273-
let branch_name = head.shorthand()
274-
.context("Detached HEAD state")?;
307+
let branch_name = head.shorthand().context("Detached HEAD state")?;
275308

276309
// Fetch from remote
277310
let mut callbacks = git2::RemoteCallbacks::new();
@@ -299,9 +332,8 @@ impl GitManager {
299332
let mut reference = repo.find_reference(&refname)?;
300333
reference.set_target(remote_commit.id(), "Fast-forward pull")?;
301334

302-
let checkout_result = repo.checkout_head(Some(
303-
git2::build::CheckoutBuilder::default().safe()
304-
));
335+
let checkout_result =
336+
repo.checkout_head(Some(git2::build::CheckoutBuilder::default().safe()));
305337

306338
if let Err(e) = checkout_result {
307339
reference.set_target(head.target().unwrap(), "Revert failed pull")?;
@@ -318,18 +350,22 @@ impl GitManager {
318350
let mut index = repo.index()?;
319351

320352
if index.has_conflicts() {
321-
let conflicts: Vec<String> = index.conflicts()?
353+
let conflicts: Vec<String> = index
354+
.conflicts()?
322355
.filter_map(|c| c.ok())
323356
.filter_map(|c| c.our.or(c.their).or(c.ancestor))
324357
.filter_map(|entry| String::from_utf8(entry.path).ok())
325358
.collect();
326359

327-
let checkout_result = repo.checkout_index(None, Some(
328-
git2::build::CheckoutBuilder::default()
329-
.allow_conflicts(true)
330-
.conflict_style_merge(true)
331-
.safe()
332-
));
360+
let checkout_result = repo.checkout_index(
361+
None,
362+
Some(
363+
git2::build::CheckoutBuilder::default()
364+
.allow_conflicts(true)
365+
.conflict_style_merge(true)
366+
.safe(),
367+
),
368+
);
333369

334370
if let Err(e) = checkout_result {
335371
repo.cleanup_state()?;
@@ -344,9 +380,9 @@ impl GitManager {
344380
let tree_id = index.write_tree()?;
345381
let tree = repo.find_tree(tree_id)?;
346382

347-
let sig = repo.signature().or_else(|_| {
348-
git2::Signature::now("Anycode User", "user@anycode.dev")
349-
})?;
383+
let sig = repo
384+
.signature()
385+
.or_else(|_| git2::Signature::now("Anycode User", "user@anycode.dev"))?;
350386

351387
let local_commit = head.peel_to_commit()?;
352388
let remote_commit_obj = repo.find_commit(remote_commit.id())?;
@@ -357,7 +393,7 @@ impl GitManager {
357393
&sig,
358394
&format!("Merge remote-tracking branch 'origin/{}'", branch_name),
359395
&tree,
360-
&[&local_commit, &remote_commit_obj]
396+
&[&local_commit, &remote_commit_obj],
361397
)?;
362398

363399
repo.cleanup_state()?;
@@ -384,15 +420,13 @@ impl GitManager {
384420

385421
let statuses = repo.statuses(Some(&mut opts))?;
386422
let is_new_file = statuses.iter().any(|entry| {
387-
entry.status().contains(Status::WT_NEW) ||
388-
entry.status().contains(Status::INDEX_NEW)
423+
entry.status().contains(Status::WT_NEW) || entry.status().contains(Status::INDEX_NEW)
389424
});
390425

391426
if is_new_file {
392427
let full_path = repo_root.join(relative_path);
393428
if full_path.exists() {
394-
std::fs::remove_file(&full_path)
395-
.context("Failed to delete untracked file")?;
429+
std::fs::remove_file(&full_path).context("Failed to delete untracked file")?;
396430
}
397431
info!("Git revert: deleted untracked file {}", path);
398432
} else {

anycode/components/ChangesPanel.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,18 @@ const statusColors: Record<string, string> = {
3333
conflict: 'status-conflict',
3434
};
3535

36-
const getFileName = (path: string): string => {
37-
const parts = path.split('/');
38-
return parts[parts.length - 1];
39-
};
40-
4136
const getDirectory = (path: string): string => {
4237
const parts = path.split('/');
4338
if (parts.length <= 1) return '';
4439
return parts.slice(0, -1).join('/');
4540
};
4641

42+
const getDisplayName = (path: string): string => {
43+
const normalized = path.replace(/\\/g, '/');
44+
const parts = normalized.split('/');
45+
return parts[parts.length - 1] || path;
46+
};
47+
4748
export const ChangesPanel: React.FC<ChangesPanelProps> = ({
4849
files,
4950
branch,
@@ -194,7 +195,7 @@ export const ChangesPanel: React.FC<ChangesPanelProps> = ({
194195
</span>
195196
<div className="changes-file-info">
196197
<span className="changes-filename">
197-
{getFileName(file.path)}
198+
{getDisplayName(file.path)}
198199
</span>
199200
<span className="changes-directory">
200201
{getDirectory(file.path)}

0 commit comments

Comments
 (0)