Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fixed git submodule bug and added tests
  • Loading branch information
Choudhry18 committed Jan 26, 2026
commit 4d1b53a2cddcb7ad981b34f7bd9b4129a5e3b4bf
13 changes: 13 additions & 0 deletions crates/uv-git/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,8 @@ impl GitCheckout {
/// *doesn't* exist, and then once we're done we create the file.
///
/// [`.ok`]: CHECKOUT_READY_LOCK
/// `git reset --hard [<commit>]` can break relative submodule URLs. Prime submodules before
/// the reset so subsequent updates use resolved URLs.
fn reset(&self, with_lfs: Option<bool>) -> Result<Option<bool>> {
let ok_file = self.repo.path.join(CHECKOUT_READY_LOCK);
let _ = paths::remove_file(&ok_file);
Expand All @@ -488,6 +490,17 @@ impl GitCheckout {
// as smudge filters can trigger on a reset even if lfs artifacts
// were not originally "fetched".
let lfs_skip_smudge = if with_lfs == Some(true) { "0" } else { "1" };

// Update submodules (`git submodule update --recursive --init`) before reset.
ProcessBuilder::new(GIT.as_ref()?)
.arg("submodule")
.arg("update")
.arg("--recursive")
.arg("--init")
.env(EnvVars::GIT_LFS_SKIP_SMUDGE, lfs_skip_smudge)
.cwd(&self.repo.path)
.exec_with_output()
.map(drop)?;
debug!("Reset {} to {}", self.repo.path.display(), self.revision);

// Perform the hard reset.
Expand Down
148 changes: 148 additions & 0 deletions crates/uv/tests/it/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11017,6 +11017,154 @@ fn unsupported_git_scheme() {
);
}

#[test]
#[cfg(unix)]
#[cfg(feature = "git")]
fn test_git_submodule_relative_url() -> Result<()> {
let context = TestContext::new("3.12");
let temp_dir = &context.temp_dir;
let utilities_dir = temp_dir.child("utilities");
utilities_dir.create_dir_all()?;

// Create and initialize the helper repository
let helpers_dir = utilities_dir.child("helpers");
helpers_dir.create_dir_all()?;

// Initialize helpers as a git repo
Command::new("git")
.args(["init"])
.current_dir(helpers_dir.path())
.output()?;

// Create a simple file in helpers
helpers_dir
.child("helper.py")
.write_str("def help():\n return 'I am a helper'")?;

// Add and commit in helpers
Command::new("git")
.args(["add", "."])
.current_dir(helpers_dir.path())
.output()?;

Command::new("git")
.args(["commit", "-m", "Initial helpers commit"])
.current_dir(helpers_dir.path())
.env("GIT_AUTHOR_NAME", "Test")
.env("GIT_AUTHOR_EMAIL", "test@example.com")
.env("GIT_COMMITTER_NAME", "Test")
.env("GIT_COMMITTER_EMAIL", "test@example.com")
.output()?;

// Create and initialize the main repository
let mylib_dir = temp_dir.child("mylib");
mylib_dir.create_dir_all()?;

// Initialize mylib as a git repo
Command::new("git")
.args(["init"])
.current_dir(mylib_dir.path())
.output()?;

// Create a simple setup.py
mylib_dir.child("setup.py").write_str(
r#"
from setuptools import setup, find_packages

setup(
name="mylib",
version="0.1.0",
packages=find_packages(),
)
"#,
)?;

// Create a simple module file
let mylib_package = mylib_dir.child("mylib");
mylib_package.create_dir_all()?;
mylib_package.child("__init__.py").write_str(
r#"
from .helpers import helper

def main():
return f"Hello from mylib, {helper.help()}"
"#,
)?;

// Add and commit in mylib
Command::new("git")
.args(["add", "."])
.current_dir(mylib_dir.path())
.output()?;

Command::new("git")
.args(["commit", "-m", "Initial mylib commit with submodule"])
.current_dir(mylib_dir.path())
.env("GIT_AUTHOR_NAME", "Test")
.env("GIT_AUTHOR_EMAIL", "test@example.com")
.env("GIT_COMMITTER_NAME", "Test")
.env("GIT_COMMITTER_EMAIL", "test@example.com")
.output()?;

// Attempt to install from the git repository with uv
// Create a simple file in helpers
helpers_dir
.child("second.py")
.write_str("def help():\n return 'I am a helper'")?;

// Add and commit in helpers
Command::new("git")
.args(["add", "."])
.current_dir(helpers_dir.path())
.output()?;

Command::new("git")
.args(["commit", "-m", "submodule commit"])
.current_dir(helpers_dir.path())
.env("GIT_AUTHOR_NAME", "Test")
.env("GIT_AUTHOR_EMAIL", "test@example.com")
.env("GIT_COMMITTER_NAME", "Test")
.env("GIT_COMMITTER_EMAIL", "test@example.com")
.output()?;

let mut filters = context.filters();
filters.push((r"@[0-9a-f]{40}", "@COMMIT_HASH"));

uv_snapshot!(filters, context.pip_install()
.arg(format!("git+file://{}", mylib_dir.path().display())), @r"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ mylib==0.1.0 (from git+file://[TEMP_DIR]/mylib@COMMIT_HASH)
");

Ok(())
}

#[test]
#[cfg(feature = "git")]
fn test_remote_git_submodule_relative_url() {
let context = TestContext::new("3.13");

uv_snapshot!(context.filters(), context.pip_install()
.arg("git+https://github.com/Choudhry18/uv-test.git"), @r"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ uv-test==0.1.0 (from git+https://github.com/Choudhry18/uv-test.git@62466a857b092f473ad4d406567fd272d819cbdc)
");
}

/// Modify a project to use a `src` layout.
#[test]
fn change_layout_src() -> Result<()> {
Expand Down
20 changes: 20 additions & 0 deletions crates/uv/tests/it/tool_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4550,3 +4550,23 @@ fn tool_install_python_platform() {
Installed 2 executables: black, blackd
");
}

#[test]
#[cfg(feature = "git")]
fn tool_install_git_relative_submodules() {
let context = TestContext::new("3.13");

uv_snapshot!(context.filters(), context.tool_install()
.arg("git+https://github.com/Choudhry18/uv-test.git"), @r"
success: false
exit_code: 1
----- stdout -----
No executables are provided by `uv-test`

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ uv-test==0.1.0 (from git+https://github.com/Choudhry18/uv-test.git@62466a857b092f473ad4d406567fd272d819cbdc)
");
}