Skip to content

Commit 850a9d4

Browse files
committed
Check in package/publish if a git submodule is dirty.
1 parent 85a52ce commit 850a9d4

File tree

2 files changed

+152
-1
lines changed

2 files changed

+152
-1
lines changed

src/cargo/ops/cargo_package.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,32 @@ fn check_repo_state(
267267
allow_dirty: bool,
268268
) -> CargoResult<Option<String>> {
269269
let workdir = repo.workdir().unwrap();
270+
271+
let mut sub_repos = Vec::new();
272+
open_submodules(repo, &mut sub_repos)?;
273+
// Sort so that longest paths are first, to check nested submodules first.
274+
sub_repos.sort_unstable_by(|a, b| b.0.as_os_str().len().cmp(&a.0.as_os_str().len()));
275+
let submodule_dirty = |path: &Path| -> bool {
276+
sub_repos
277+
.iter()
278+
.filter(|(sub_path, _sub_repo)| path.starts_with(sub_path))
279+
.any(|(sub_path, sub_repo)| {
280+
let relative = path.strip_prefix(sub_path).unwrap();
281+
sub_repo
282+
.status_file(relative)
283+
.map(|status| status != git2::Status::CURRENT)
284+
.unwrap_or(false)
285+
})
286+
};
287+
270288
let dirty = src_files
271289
.iter()
272290
.filter(|file| {
273291
let relative = file.strip_prefix(workdir).unwrap();
274292
if let Ok(status) = repo.status_file(relative) {
275293
status != git2::Status::CURRENT
276294
} else {
277-
false
295+
submodule_dirty(file)
278296
}
279297
})
280298
.map(|path| {
@@ -300,6 +318,22 @@ fn check_repo_state(
300318
Ok(None)
301319
}
302320
}
321+
322+
/// Helper to recursively open all submodules.
323+
fn open_submodules(
324+
repo: &git2::Repository,
325+
sub_repos: &mut Vec<(PathBuf, git2::Repository)>,
326+
) -> CargoResult<()> {
327+
for submodule in repo.submodules()? {
328+
// Ignore submodules that don't open, they are probably not initialized.
329+
// If its files are required, then the verification step should fail.
330+
if let Ok(sub_repo) = submodule.open() {
331+
open_submodules(&sub_repo, sub_repos)?;
332+
sub_repos.push((sub_repo.workdir().unwrap().to_owned(), sub_repo));
333+
}
334+
}
335+
Ok(())
336+
}
303337
}
304338

305339
// Checks for and `bail!` if a source file matches `ROOT/VCS_INFO_FILE`, since

tests/testsuite/git.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2691,3 +2691,120 @@ fn git_fetch_cli_env_clean() {
26912691
.env("GIT_DIR", git_proj.root().join(".git"))
26922692
.run();
26932693
}
2694+
2695+
#[cargo_test]
2696+
fn dirty_submodule() {
2697+
// `cargo package` warns for dirty file in submodule.
2698+
let git_project = git::new("foo", |project| {
2699+
project
2700+
.file("Cargo.toml", &basic_manifest("foo", "0.5.0"))
2701+
// This is necessary because `git::add` is too eager.
2702+
.file(".gitignore", "/target")
2703+
})
2704+
.unwrap();
2705+
let git_project2 = git::new("src", |project| {
2706+
project.no_manifest().file("lib.rs", "pub fn f() {}")
2707+
})
2708+
.unwrap();
2709+
2710+
let repo = git2::Repository::open(&git_project.root()).unwrap();
2711+
let url = path2url(git_project2.root()).to_string();
2712+
git::add_submodule(&repo, &url, Path::new("src"));
2713+
2714+
// Submodule added, but not committed.
2715+
git_project
2716+
.cargo("package --no-verify")
2717+
.with_status(101)
2718+
.with_stderr(
2719+
"\
2720+
[WARNING] manifest has no [..]
2721+
See [..]
2722+
[ERROR] 1 files in the working directory contain changes that were not yet committed into git:
2723+
2724+
.gitmodules
2725+
2726+
to proceed despite [..]
2727+
",
2728+
)
2729+
.run();
2730+
2731+
git::commit(&repo);
2732+
git_project.cargo("package --no-verify").run();
2733+
2734+
// Modify file, check for warning.
2735+
git_project.change_file("src/lib.rs", "");
2736+
git_project
2737+
.cargo("package --no-verify")
2738+
.with_status(101)
2739+
.with_stderr(
2740+
"\
2741+
[WARNING] manifest has no [..]
2742+
See [..]
2743+
[ERROR] 1 files in the working directory contain changes that were not yet committed into git:
2744+
2745+
src/lib.rs
2746+
2747+
to proceed despite [..]
2748+
",
2749+
)
2750+
.run();
2751+
// Commit the change.
2752+
let sub_repo = git2::Repository::open(git_project.root().join("src")).unwrap();
2753+
git::add(&sub_repo);
2754+
git::commit(&sub_repo);
2755+
git::add(&repo);
2756+
git::commit(&repo);
2757+
git_project.cargo("package --no-verify").run();
2758+
2759+
// Try with a nested submodule.
2760+
let git_project3 = git::new("bar", |project| project.no_manifest().file("mod.rs", "")).unwrap();
2761+
let url = path2url(git_project3.root()).to_string();
2762+
git::add_submodule(&sub_repo, &url, Path::new("bar"));
2763+
git_project
2764+
.cargo("package --no-verify")
2765+
.with_status(101)
2766+
.with_stderr(
2767+
"\
2768+
[WARNING] manifest has no [..]
2769+
See [..]
2770+
[ERROR] 1 files in the working directory contain changes that were not yet committed into git:
2771+
2772+
src/.gitmodules
2773+
2774+
to proceed despite [..]
2775+
",
2776+
)
2777+
.run();
2778+
2779+
// Commit the submodule addition.
2780+
git::commit(&sub_repo);
2781+
git::add(&repo);
2782+
git::commit(&repo);
2783+
git_project.cargo("package --no-verify").run();
2784+
// Modify within nested submodule.
2785+
git_project.change_file("src/bar/mod.rs", "//test");
2786+
git_project
2787+
.cargo("package --no-verify")
2788+
.with_status(101)
2789+
.with_stderr(
2790+
"\
2791+
[WARNING] manifest has no [..]
2792+
See [..]
2793+
[ERROR] 1 files in the working directory contain changes that were not yet committed into git:
2794+
2795+
src/bar/mod.rs
2796+
2797+
to proceed despite [..]
2798+
",
2799+
)
2800+
.run();
2801+
// And commit the change.
2802+
let sub_sub_repo = git2::Repository::open(git_project.root().join("src/bar")).unwrap();
2803+
git::add(&sub_sub_repo);
2804+
git::commit(&sub_sub_repo);
2805+
git::add(&sub_repo);
2806+
git::commit(&sub_repo);
2807+
git::add(&repo);
2808+
git::commit(&repo);
2809+
git_project.cargo("package --no-verify").run();
2810+
}

0 commit comments

Comments
 (0)