From 3152253d10e4562eab5b4ba7ebee3b76ce074fa4 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 16 Feb 2026 11:31:57 +0000 Subject: [PATCH] Fix workspace discovery with gitignored files in subdirectories --- crates/uv-workspace/src/workspace.rs | 4 +- crates/uv/tests/it/workspace.rs | 75 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/crates/uv-workspace/src/workspace.rs b/crates/uv-workspace/src/workspace.rs index e1a1da6096e9e..4d4559e44f04c 100644 --- a/crates/uv-workspace/src/workspace.rs +++ b/crates/uv-workspace/src/workspace.rs @@ -1617,8 +1617,8 @@ fn has_only_gitignored_files(path: &Path) -> bool { return false; }; - // Skip the root directory itself. - if entry.path() == path { + // Skip directories. + if entry.path().is_dir() { continue; } diff --git a/crates/uv/tests/it/workspace.rs b/crates/uv/tests/it/workspace.rs index 839ad050bf8f7..1f2cdb6dd57f1 100644 --- a/crates/uv/tests/it/workspace.rs +++ b/crates/uv/tests/it/workspace.rs @@ -962,6 +962,81 @@ fn workspace_gitignored_member() -> Result<()> { Ok(()) } +/// Ensure that workspace discovery skips directories that only contain gitignored +/// files even if they're nested inside non-ignored directories. +#[test] +fn workspace_gitignored_member_in_subdirectory() -> Result<()> { + let context = uv_test::test_context!("3.12"); + + // Build the main workspace ... + let workspace = context.temp_dir.child("workspace"); + workspace.child("pyproject.toml").write_str(indoc! {r#" + [tool.uv.workspace] + members = ["packages/*"] + "#})?; + + // ... with a `.gitignore` that ignores `__pycache__` ... + workspace.child(".gitignore").write_str("__pycache__/\n")?; + + // ... with a ... + let deps = indoc! {r#" + dependencies = ["b"] + + [tool.uv.sources] + b = { workspace = true } + "#}; + make_project(&workspace.join("packages").join("a"), "a", deps)?; + + // ... and b. + let deps = indoc! {r" + "}; + make_project(&workspace.join("packages").join("b"), "b", deps)?; + + // ... and a c that only contains gitignored files. + fs_err::create_dir_all( + workspace + .join("packages") + .join("c") + .join("foo") + .join("__pycache__"), + )?; + fs_err::write( + workspace + .join("packages") + .join("c") + .join("foo") + .join("__pycache__") + .join("test.cpython-312.pyc"), + "fake", + )?; + + uv_snapshot!(context.filters(), context.lock().current_dir(&workspace), @" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.12.[X] interpreter at: [PYTHON-3.12] + Resolved 2 packages in [TIME] + " + ); + + let lock: SourceLock = toml::from_str(&fs_err::read_to_string(workspace.join("uv.lock"))?)?; + + assert_json_snapshot!(lock.sources(), @r#" + { + "a": { + "editable": "packages/a" + }, + "b": { + "editable": "packages/b" + } + } + "#); + + Ok(()) +} + /// Ensure that workspace discovery skips directories that only contain files ignored via /// `.ignore` (not just `.gitignore`). #[test]