|
1 | 1 | //! Helpers to gather the VCS information for `cargo package`.
|
2 | 2 |
|
| 3 | +use std::collections::HashSet; |
3 | 4 | use std::path::Path;
|
4 | 5 | use std::path::PathBuf;
|
5 | 6 |
|
@@ -133,12 +134,14 @@ fn git(
|
133 | 134 | // Find the intersection of dirty in git, and the src_files that would
|
134 | 135 | // be packaged. This is a lazy n^2 check, but seems fine with
|
135 | 136 | // thousands of files.
|
| 137 | + let mut dirty_files_outside_pkg_root = dirty_symlinks(pkg, repo, src_files)?; |
| 138 | + dirty_files_outside_pkg_root.extend(dirty_metadata_paths(pkg, repo)?); |
136 | 139 | let cwd = gctx.cwd();
|
137 | 140 | let mut dirty_src_files: Vec<_> = src_files
|
138 | 141 | .iter()
|
139 | 142 | .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path)))
|
140 | 143 | .map(|p| p.as_ref())
|
141 |
| - .chain(dirty_metadata_paths(pkg, repo)?.iter()) |
| 144 | + .chain(dirty_files_outside_pkg_root.iter()) |
142 | 145 | .map(|path| {
|
143 | 146 | pathdiff::diff_paths(path, cwd)
|
144 | 147 | .as_ref()
|
@@ -206,6 +209,36 @@ fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult<V
|
206 | 209 | Ok(dirty_files)
|
207 | 210 | }
|
208 | 211 |
|
| 212 | +/// Checks whether source files are symlinks and have been modified. |
| 213 | +/// |
| 214 | +/// This is required because those paths may link to a file outside the |
| 215 | +/// current package root, but still under the git workdir, affecting the |
| 216 | +/// final packaged `.crate` file. |
| 217 | +fn dirty_symlinks( |
| 218 | + pkg: &Package, |
| 219 | + repo: &git2::Repository, |
| 220 | + src_files: &[PathEntry], |
| 221 | +) -> CargoResult<HashSet<PathBuf>> { |
| 222 | + let workdir = repo.workdir().unwrap(); |
| 223 | + let mut dirty_symlinks = HashSet::new(); |
| 224 | + for rel_path in src_files |
| 225 | + .iter() |
| 226 | + .filter(|p| p.is_symlink_or_under_symlink()) |
| 227 | + .map(|p| p.as_ref().as_path()) |
| 228 | + // If inside package root. Don't bother checking git status. |
| 229 | + .filter(|p| paths::strip_prefix_canonical(*p, pkg.root()).is_err()) |
| 230 | + // Handle files outside package root but under git workdir, |
| 231 | + .filter_map(|p| paths::strip_prefix_canonical(p, workdir).ok()) |
| 232 | + { |
| 233 | + // TODO: Should we warn users if there are like thousands of symlinks, |
| 234 | + // which may hurt the performance? |
| 235 | + if repo.status_file(&rel_path)? != git2::Status::CURRENT { |
| 236 | + dirty_symlinks.insert(workdir.join(rel_path)); |
| 237 | + } |
| 238 | + } |
| 239 | + Ok(dirty_symlinks) |
| 240 | +} |
| 241 | + |
209 | 242 | /// Helper to collect dirty statuses for a single repo.
|
210 | 243 | fn collect_statuses(repo: &git2::Repository, dirty_files: &mut Vec<PathBuf>) -> CargoResult<()> {
|
211 | 244 | let mut status_opts = git2::StatusOptions::new();
|
|
0 commit comments