Compare commits
4 Commits
507c61fe5f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| aced95a845 | |||
| e648b86c93 | |||
| aafc847d02 | |||
| 1eda5a9642 |
@@ -32,11 +32,12 @@ pub fn add(files: Vec<PathBuf>) -> Result<(), TourError> {
|
|||||||
.open(STAGED_PATH)?;
|
.open(STAGED_PATH)?;
|
||||||
|
|
||||||
for file in &files {
|
for file in &files {
|
||||||
if existing_set.contains(file) {
|
let normalized: PathBuf = file.components().collect();
|
||||||
println!("already staged: {}", file.display());
|
if existing_set.contains(&normalized) {
|
||||||
|
println!("already staged: {}", normalized.display());
|
||||||
} else {
|
} else {
|
||||||
writeln!(staged, "{}", file.display())?;
|
writeln!(staged, "{}", normalized.display())?;
|
||||||
println!("staged: {}", file.display());
|
println!("staged: {}", normalized.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ impl std::fmt::Display for TourError {
|
|||||||
write!(f, "Cannot end a tour with no steps. Use `tour commit` to add steps first.")
|
write!(f, "Cannot end a tour with no steps. Use `tour commit` to add steps first.")
|
||||||
}
|
}
|
||||||
Self::NotADescendant(p) => {
|
Self::NotADescendant(p) => {
|
||||||
write!(f, "File {:?} is not a descendant of the working directory.", p)
|
write!(f, "File {} is not a descendant of the working directory.", p.display())
|
||||||
}
|
}
|
||||||
Self::InsideTourDir(p) => {
|
Self::InsideTourDir(p) => {
|
||||||
write!(f, "File {:?} is inside a .tour directory, which is not allowed.", p)
|
write!(f, "File {} is inside a .tour directory, which is not allowed.", p.display())
|
||||||
}
|
}
|
||||||
Self::FileNotFound(p) => write!(f, "File not found: {}", p.display()),
|
Self::FileNotFound(p) => write!(f, "File not found: {}", p.display()),
|
||||||
Self::StepOutOfRange { step, total } => {
|
Self::StepOutOfRange { step, total } => {
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ pub fn list() -> Result<(), TourError> {
|
|||||||
|
|
||||||
for i in 0..total {
|
for i in 0..total {
|
||||||
let step_dir = Path::new(TOUR_DIR).join("steps").join(i.to_string());
|
let step_dir = Path::new(TOUR_DIR).join("steps").join(i.to_string());
|
||||||
let message = fs::read_to_string(step_dir.join("message")).unwrap_or_default();
|
let message = fs::read_to_string(step_dir.join("message"))
|
||||||
|
.unwrap_or_else(|_| "(no message)".into());
|
||||||
println!(" {}. {}", i + 1, message.trim());
|
println!(" {}. {}", i + 1, message.trim());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
25
src/step.rs
25
src/step.rs
@@ -59,17 +59,28 @@ fn go_to_step(target: u32, total: u32) -> Result<(), TourError> {
|
|||||||
let tracked = get_tracked_files()?;
|
let tracked = get_tracked_files()?;
|
||||||
let old_files = snapshot_tracked_files(&cwd, &tracked)?;
|
let old_files = snapshot_tracked_files(&cwd, &tracked)?;
|
||||||
|
|
||||||
remove_tracked_files(&cwd, &tracked)?;
|
// Stage new step files to a temp dir first — if this fails, working files are untouched
|
||||||
|
|
||||||
// Copy step contents into CWD (skipping the message file)
|
|
||||||
let step_dir = Path::new(TOUR_DIR).join("steps").join(target.to_string());
|
let step_dir = Path::new(TOUR_DIR).join("steps").join(target.to_string());
|
||||||
|
let tmp_dir = Path::new(TOUR_DIR).join("tmp_step");
|
||||||
|
if tmp_dir.exists() {
|
||||||
|
fs::remove_dir_all(&tmp_dir)?;
|
||||||
|
}
|
||||||
|
fs::create_dir_all(&tmp_dir)?;
|
||||||
for entry in fs::read_dir(&step_dir)? {
|
for entry in fs::read_dir(&step_dir)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
if entry.file_name() == "message" {
|
if entry.file_name() == "message" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
copy_tree(&entry.path(), &tmp_dir.join(entry.file_name()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now safe to remove old tracked files and install the new ones
|
||||||
|
remove_tracked_files(&cwd, &tracked)?;
|
||||||
|
for entry in fs::read_dir(&tmp_dir)? {
|
||||||
|
let entry = entry?;
|
||||||
copy_tree(&entry.path(), &cwd.join(entry.file_name()))?;
|
copy_tree(&entry.path(), &cwd.join(entry.file_name()))?;
|
||||||
}
|
}
|
||||||
|
fs::remove_dir_all(&tmp_dir)?;
|
||||||
|
|
||||||
// Persist the new step
|
// Persist the new step
|
||||||
fs::write(SESSION_PATH, format!("STEP={}", target))?;
|
fs::write(SESSION_PATH, format!("STEP={}", target))?;
|
||||||
@@ -77,7 +88,8 @@ fn go_to_step(target: u32, total: u32) -> Result<(), TourError> {
|
|||||||
let new_files = snapshot_tracked_files(&cwd, &tracked)?;
|
let new_files = snapshot_tracked_files(&cwd, &tracked)?;
|
||||||
print_changes(&old_files, &new_files);
|
print_changes(&old_files, &new_files);
|
||||||
|
|
||||||
let message = fs::read_to_string(step_dir.join("message")).unwrap_or_default();
|
let message = fs::read_to_string(step_dir.join("message"))
|
||||||
|
.unwrap_or_else(|_| "(no message)".into());
|
||||||
println!(
|
println!(
|
||||||
"\n{BOLD}Step {}/{total}:{RESET} {}",
|
"\n{BOLD}Step {}/{total}:{RESET} {}",
|
||||||
target + 1,
|
target + 1,
|
||||||
@@ -195,6 +207,11 @@ fn print_diff(old: &str, new: &str) {
|
|||||||
let m = old_lines.len();
|
let m = old_lines.len();
|
||||||
let n = new_lines.len();
|
let n = new_lines.len();
|
||||||
|
|
||||||
|
if m > 1000 || n > 1000 {
|
||||||
|
println!(" (file changed)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// LCS table
|
// LCS table
|
||||||
let mut dp = vec![vec![0usize; n + 1]; m + 1];
|
let mut dp = vec![vec![0usize; n + 1]; m + 1];
|
||||||
for i in 1..=m {
|
for i in 1..=m {
|
||||||
|
|||||||
11
src/utils.rs
11
src/utils.rs
@@ -17,8 +17,8 @@ pub fn get_current_step() -> Option<u32> {
|
|||||||
fs::read_to_string(SESSION_PATH)
|
fs::read_to_string(SESSION_PATH)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| {
|
.and_then(|s| {
|
||||||
s.split("STEP=")
|
s.lines()
|
||||||
.nth(1)
|
.find_map(|l| l.strip_prefix("STEP="))
|
||||||
.and_then(|v| v.trim().parse::<u32>().ok())
|
.and_then(|v| v.trim().parse::<u32>().ok())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,12 @@ pub fn get_tour_step() -> Result<u32, TourError> {
|
|||||||
pub fn copy_path(src: &Path, dest_dir: &Path) -> Result<(), io::Error> {
|
pub fn copy_path(src: &Path, dest_dir: &Path) -> Result<(), io::Error> {
|
||||||
let relative_src = if src.is_absolute() {
|
let relative_src = if src.is_absolute() {
|
||||||
let cwd = std::env::current_dir()?;
|
let cwd = std::env::current_dir()?;
|
||||||
src.strip_prefix(&cwd).unwrap_or(src).to_path_buf()
|
src.strip_prefix(&cwd)
|
||||||
|
.map_err(|_| io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
format!("path '{}' is not under the current directory", src.display()),
|
||||||
|
))?
|
||||||
|
.to_path_buf()
|
||||||
} else {
|
} else {
|
||||||
src.to_path_buf()
|
src.to_path_buf()
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user