fix: make the link store hold what it says it holds
This commit is contained in:
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use super::store::{Repo, resolve, symlink_metadata_opt};
|
||||
use super::store::{Leads, Repo, leads, resolve, symlink_metadata_opt};
|
||||
use crate::cmd::{Cmd, LsFiles};
|
||||
use crate::ctx::Ctx;
|
||||
use crate::output::{line, text, write_bytes};
|
||||
@@ -33,6 +33,11 @@ pub fn check(
|
||||
}
|
||||
}
|
||||
|
||||
// and says nothing about tracked paths, where a symlink out still counts
|
||||
for entry in list_tracked(ctx, &repo, &pathspecs)? {
|
||||
exposed.extend(classify_tracked(&repo, &path_from(&entry))?);
|
||||
}
|
||||
|
||||
exposed.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
if porcelain || null {
|
||||
@@ -139,6 +144,8 @@ impl Section {
|
||||
const UNTRACKED: char = '?';
|
||||
const IGNORED: char = '!';
|
||||
const ELSEWHERE: char = '>';
|
||||
// no git equivalent: a tracked path, which only ever appears as a symlink out
|
||||
const TRACKED: char = 'T';
|
||||
|
||||
struct Exposed {
|
||||
mark: char,
|
||||
@@ -178,7 +185,16 @@ fn classify(repo: &Repo, rel: &Path, mark: char) -> Result<Option<Exposed>> {
|
||||
|
||||
let dest = read_link(&src)?;
|
||||
if dest == repo.store.join(rel) {
|
||||
return Ok(None);
|
||||
// it names the store, but what the store holds there can be a symlink of
|
||||
// its own leading straight back out, which is not being held at all
|
||||
return Ok(match leads(&src, &repo.store) {
|
||||
Leads::Inside | Leads::Dangling => None,
|
||||
Leads::Outside(end) => Some(Exposed {
|
||||
mark,
|
||||
name,
|
||||
dest: Some(end),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Some(Exposed {
|
||||
@@ -188,6 +204,29 @@ fn classify(repo: &Repo, rel: &Path, mark: char) -> Result<Option<Exposed>> {
|
||||
}))
|
||||
}
|
||||
|
||||
// git tracks symlinks, so one can lead out of the repository without ever
|
||||
// showing up in an untracked or ignored listing; `add` cannot externalize it
|
||||
// either, so all check can do is say it is there
|
||||
fn classify_tracked(repo: &Repo, rel: &Path) -> Result<Option<Exposed>> {
|
||||
let src = repo.root.join(rel);
|
||||
|
||||
let Some(meta) = symlink_metadata_opt(&src)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
if !meta.is_symlink() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(match leads(&src, &repo.root) {
|
||||
Leads::Inside | Leads::Dangling => None,
|
||||
Leads::Outside(end) => Some(Exposed {
|
||||
mark: TRACKED,
|
||||
name: rel.to_path_buf(),
|
||||
dest: Some(end),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
fn walk(repo: &Repo, rel: &Path, mark: char) -> Result<(usize, Vec<Exposed>)> {
|
||||
let dir = repo.root.join(rel);
|
||||
let mut handled = 0;
|
||||
@@ -210,8 +249,12 @@ fn walk(repo: &Repo, rel: &Path, mark: char) -> Result<(usize, Vec<Exposed>)> {
|
||||
}
|
||||
}
|
||||
|
||||
// nothing below is in the store, so collapse to one line
|
||||
if handled == 0 && !exposed.is_empty() {
|
||||
// nothing below is in the store, so collapse to one line -- unless some of
|
||||
// it leads out of the repository, which is a different thing to report and
|
||||
// carries a destination the one line would drop
|
||||
let all_content = exposed.iter().all(|item| item.dest.is_none());
|
||||
|
||||
if handled == 0 && !exposed.is_empty() && all_content {
|
||||
let mut name = rel.as_os_str().to_owned();
|
||||
name.push("/");
|
||||
|
||||
@@ -257,6 +300,12 @@ fn list_others(
|
||||
Ok(split_nul(&listing.capture_bytes(ctx)?))
|
||||
}
|
||||
|
||||
fn list_tracked(ctx: &Ctx, repo: &Repo, pathspecs: &[PathBuf]) -> Result<Vec<Vec<u8>>> {
|
||||
let listing = LsFiles::tracked(&repo.root).limited_to(pathspecs);
|
||||
|
||||
Ok(split_nul(&listing.capture_bytes(ctx)?))
|
||||
}
|
||||
|
||||
// every listing check reads is asked for with -z, and kept as the bytes git
|
||||
// wrote: a path is not obliged to be utf-8, and one that is not used to fail
|
||||
// the whole listing rather than the one entry
|
||||
|
||||
Reference in New Issue
Block a user