Summary
mdref mv <source> <dest> fails with IO error reading '<./source>': No such file or directory whenever the source file contains a Markdown link that references itself (a self-reference) and the caller passes source as a relative path that does not include a leading ./. The operation is rolled back, so no data is lost, but the move does not complete.
Steps to Reproduce
-
From the repo root:
cat examples/main.md | head -n 8
# Outer main
#
# 
#
# 
#
# [self](main.md) [inner](inner/main.md) [sub](inner/sub/main.md) ← self-reference on line 7
-
Build and run mv:
cargo build --bin mdref
./target/debug/mdref mv examples/main.md ./test.md
-
Observe the error:
Move examples/main.md -> ./test.md in .
Error: IO error reading './examples/main.md': No such file or directory (os error 2)
-
ls examples/main.md ./test.md — the source file is still there (rolled back), the destination was not created.
Calling mdref find examples/main.md works fine on the exact same path, which makes the failure surprising.
Expected Behavior
mdref mv examples/main.md ./test.md should succeed, move the file to ./test.md, rewrite the self-reference in the moved file, and update every external reference — exactly like it does when source happens to already be canonical or is passed with a ./ prefix.
Actual Behavior
The command always fails during the apply_replacements phase with the above IoRead error, regardless of whether a self-reference is actually present or not, as long as the source path as passed by the user differs textually from the path shape produced by WalkBuilder. The MoveTransaction rollback kicks in and restores the source file.
Root cause (already traced): src/core/find.rs::find_references obtains Markdown paths through src/core/util.rs::collect_markdown_files, which hands back ignore::WalkBuilder outputs verbatim. For root=".", those paths are prefixed with ./ (e.g. ./examples/main.md). src/core/mv.rs::mv_regular_file then builds replacements_by_file keyed on those ./-prefixed PathBufs, but tries to strip the self-reference entry via replacements_by_file.remove(source) using the non-prefixed source the user supplied (examples/main.md). The lookup misses, so the self-reference entry survives into the Phase-2 execute step. fs::rename moves the source away, and the subsequent apply_replacements read on the stale key fails. The same shape of mismatch exists in mv_case_only_file via move_source_replacements_to_destination.
mdref Version
0.4.4 (at commit 2ae5c5b on main, post PR #5)
Operating System
macOS
Logs / Screenshots
$ ./target/debug/mdref mv examples/main.md ./test.md
Move examples/main.md -> ./test.md in .
Error: IO error reading './examples/main.md': No such file or directory (os error 2)
$ ls examples/main.md ./test.md
ls: ./test.md: No such file or directory
examples/main.md
Summary
mdref mv <source> <dest>fails withIO error reading '<./source>': No such file or directorywhenever the source file contains a Markdown link that references itself (a self-reference) and the caller passessourceas a relative path that does not include a leading./. The operation is rolled back, so no data is lost, but the move does not complete.Steps to Reproduce
From the repo root:
Build and run
mv:Observe the error:
ls examples/main.md ./test.md— the source file is still there (rolled back), the destination was not created.Calling
mdref find examples/main.mdworks fine on the exact same path, which makes the failure surprising.Expected Behavior
mdref mv examples/main.md ./test.mdshould succeed, move the file to./test.md, rewrite the self-reference in the moved file, and update every external reference — exactly like it does whensourcehappens to already be canonical or is passed with a./prefix.Actual Behavior
The command always fails during the
apply_replacementsphase with the aboveIoReaderror, regardless of whether a self-reference is actually present or not, as long as the source path as passed by the user differs textually from the path shape produced byWalkBuilder. TheMoveTransactionrollback kicks in and restores the source file.Root cause (already traced):
src/core/find.rs::find_referencesobtains Markdown paths throughsrc/core/util.rs::collect_markdown_files, which hands backignore::WalkBuilderoutputs verbatim. Forroot=".", those paths are prefixed with./(e.g../examples/main.md).src/core/mv.rs::mv_regular_filethen buildsreplacements_by_filekeyed on those./-prefixedPathBufs, but tries to strip the self-reference entry viareplacements_by_file.remove(source)using the non-prefixedsourcethe user supplied (examples/main.md). The lookup misses, so the self-reference entry survives into the Phase-2 execute step.fs::renamemoves the source away, and the subsequentapply_replacementsread on the stale key fails. The same shape of mismatch exists inmv_case_only_fileviamove_source_replacements_to_destination.mdref Version
0.4.4 (at commit
2ae5c5bonmain, post PR #5)Operating System
macOS
Logs / Screenshots