fix: handle missing newline and slash-less variants in gitignore check
- Check for both .deepseek and .deepseek/ to prevent duplicates - Ensure trailing newline before appending to avoid joining with unterminated line - Add tests for both edge cases
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! /init command - Generate AGENTS.md for project
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::tui::app::App;
|
||||
@@ -49,19 +50,42 @@ fn ensure_deepseek_gitignored(workspace: &Path) {
|
||||
let entry = ".deepseek/";
|
||||
|
||||
// Read existing contents (if any) and check whether the entry is already present.
|
||||
// Check both with and without trailing slash to catch variants like
|
||||
// ".deepseek" and ".deepseek/".
|
||||
if let Ok(existing) = std::fs::read_to_string(&gitignore) {
|
||||
if existing.lines().any(|line| line.trim() == entry) {
|
||||
let entry_no_slash = entry.trim_end_matches('/');
|
||||
if existing.lines().any(|line| {
|
||||
let trimmed = line.trim();
|
||||
trimmed == entry || trimmed == entry_no_slash
|
||||
}) {
|
||||
return; // already ignored
|
||||
}
|
||||
}
|
||||
|
||||
// Append the entry. If .gitignore doesn't exist yet, create it with a header.
|
||||
// Ensure there's a trailing newline before our entry to avoid joining with
|
||||
// a previous unterminated line.
|
||||
use std::io::Write;
|
||||
if let Ok(mut file) = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&gitignore)
|
||||
{
|
||||
// If the file is non-empty and doesn't end with a newline, add one first.
|
||||
if let Ok(meta) = file.metadata()
|
||||
&& meta.len() > 0
|
||||
{
|
||||
// Read last byte to check for trailing newline.
|
||||
if let Ok(mut f) = std::fs::File::open(&gitignore) {
|
||||
use std::io::Seek;
|
||||
if f.seek(std::io::SeekFrom::End(-1)).is_ok() {
|
||||
let mut buf = [0u8; 1];
|
||||
if f.read_exact(&mut buf).is_ok() && buf[0] != b'\n' {
|
||||
let _ = writeln!(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = writeln!(file, "{entry}");
|
||||
}
|
||||
}
|
||||
@@ -361,4 +385,36 @@ version = "1.0.0"
|
||||
|
||||
assert!(!tmpdir.path().join(".gitignore").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_deepseek_gitignored_handles_no_trailing_newline() {
|
||||
let tmpdir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(tmpdir.path().join(".git")).unwrap();
|
||||
// Write a file that does NOT end with a newline.
|
||||
std::fs::write(tmpdir.path().join(".gitignore"), "target/").unwrap();
|
||||
|
||||
ensure_deepseek_gitignored(tmpdir.path());
|
||||
|
||||
let content = std::fs::read_to_string(tmpdir.path().join(".gitignore")).unwrap();
|
||||
// Must have both entries on separate lines.
|
||||
assert!(content.contains("target/"));
|
||||
assert!(content.contains(".deepseek/"));
|
||||
// The entries should be on different lines.
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
assert!(lines.len() >= 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_deepseek_gitignored_detects_variant_without_slash() {
|
||||
let tmpdir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(tmpdir.path().join(".git")).unwrap();
|
||||
// Write .deepseek without trailing slash.
|
||||
std::fs::write(tmpdir.path().join(".gitignore"), ".deepseek\n").unwrap();
|
||||
|
||||
ensure_deepseek_gitignored(tmpdir.path());
|
||||
|
||||
let content = std::fs::read_to_string(tmpdir.path().join(".gitignore")).unwrap();
|
||||
// Should NOT add a duplicate entry.
|
||||
assert_eq!(content.matches(".deepseek").count(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user