Skip to content

Commit 7fa835c

Browse files
authored
Merge pull request #179 from pulp-platform/phsauter/vendor
Fix mapping limitation in vendor
2 parents 779d524 + 95d9ce5 commit 7fa835c

File tree

2 files changed

+102
-37
lines changed

2 files changed

+102
-37
lines changed

src/cmd/vendor.rs

+101-36
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::error::*;
1515
use crate::git::Git;
1616
use crate::sess::{DependencySource, Session};
1717
use glob::Pattern;
18+
use std::collections::HashSet;
1819
use std::path::Path;
1920
use std::path::PathBuf;
2021
use tempfile::TempDir;
@@ -28,6 +29,8 @@ pub struct PatchLink {
2829
pub from_prefix: PathBuf,
2930
/// prefix for local
3031
pub to_prefix: PathBuf,
32+
/// subdirs and files to exclude
33+
pub exclude: Vec<PathBuf>,
3134
}
3235

3336
/// Assemble the `vendor` subcommand.
@@ -128,6 +131,7 @@ pub fn run(sess: &Session, matches: &ArgMatches) -> Result<()> {
128131
patch_dir: link.patch_dir,
129132
from_prefix: link.from,
130133
to_prefix: link.to,
134+
exclude: vec![],
131135
})
132136
}
133137

@@ -138,25 +142,59 @@ pub fn run(sess: &Session, matches: &ArgMatches) -> Result<()> {
138142
patch_dir: vendor_package.patch_dir.clone(),
139143
from_prefix: PathBuf::from(""),
140144
to_prefix: PathBuf::from(""),
145+
exclude: vec![],
141146
}],
142147
_ => patch_links,
143148
}
144149
};
145150

151+
// sort patch_links so more specific links have priority
152+
// 1. file links over directory links eg 'a/file -> c/file' before 'b/ -> c/'
153+
// 2. subdirs (deeper paths) first eg 'a/aa/ -> c/aa' before 'a/ab -> c/'
154+
let mut sorted_links: Vec<_> = patch_links.clone();
155+
sorted_links.sort_by(|a, b| {
156+
let a_is_file = a.to_prefix.is_file();
157+
let b_is_file = b.to_prefix.is_file();
158+
159+
if a_is_file != b_is_file {
160+
return b_is_file.cmp(&a_is_file);
161+
}
162+
163+
let a_depth = a.to_prefix.iter().count();
164+
let b_depth = b.to_prefix.iter().count();
165+
166+
b_depth.cmp(&a_depth)
167+
});
168+
169+
// Add all subdirs and files to the exclude list of above dirs
170+
// avoids duplicate handling of the same changes
171+
let mut seen_paths: HashSet<PathBuf> = HashSet::new();
172+
for patch_link in sorted_links.iter_mut() {
173+
patch_link.exclude = seen_paths
174+
.iter()
175+
.filter(|path| path.starts_with(&patch_link.to_prefix)) // subdir?
176+
.cloned()
177+
.collect();
178+
179+
seen_paths.insert(patch_link.to_prefix.clone());
180+
}
146181
let git = Git::new(tmp_path, &sess.config.git);
147182

148183
match matches.subcommand() {
149184
Some(("diff", matches)) => {
150185
// Apply patches
151-
patch_links.clone().into_iter().try_for_each(|patch_link| {
152-
apply_patches(&rt, git, vendor_package.name.clone(), patch_link).map(|_| ())
153-
})?;
186+
sorted_links
187+
.clone()
188+
.into_iter()
189+
.try_for_each(|patch_link| {
190+
apply_patches(&rt, git, vendor_package.name.clone(), patch_link).map(|_| ())
191+
})?;
154192

155193
// Stage applied patches to clean working tree
156194
rt.block_on(git.add_all())?;
157195

158196
// Print diff for each link
159-
patch_links.into_iter().try_for_each(|patch_link| {
197+
sorted_links.into_iter().try_for_each(|patch_link| {
160198
let get_diff = diff(&rt, git, vendor_package, patch_link, dep_path.clone())
161199
.map_err(|cause| Error::chain("Failed to get diff.", cause))?;
162200
if !get_diff.is_empty() {
@@ -176,7 +214,7 @@ pub fn run(sess: &Session, matches: &ArgMatches) -> Result<()> {
176214
}
177215

178216
Some(("init", matches)) => {
179-
patch_links.clone().into_iter().try_for_each(|patch_link| {
217+
sorted_links.into_iter().rev().try_for_each(|patch_link| {
180218
stageln!("Copying", "{} files from upstream", vendor_package.name);
181219
// Remove existing directories before importing them again
182220
let target_path = patch_link
@@ -209,7 +247,7 @@ pub fn run(sess: &Session, matches: &ArgMatches) -> Result<()> {
209247
Some(("patch", matches)) => {
210248
// Apply patches
211249
let mut num_patches = 0;
212-
patch_links
250+
sorted_links
213251
.clone()
214252
.into_iter()
215253
.try_for_each(|patch_link| {
@@ -225,7 +263,7 @@ pub fn run(sess: &Session, matches: &ArgMatches) -> Result<()> {
225263
}
226264

227265
// Generate patch
228-
patch_links.clone().into_iter().try_for_each( |patch_link| {
266+
sorted_links.into_iter().try_for_each( |patch_link| {
229267
match patch_link.patch_dir.clone() {
230268
Some(patch_dir) => {
231269
if matches.get_flag("plain") {
@@ -285,7 +323,7 @@ pub fn init(
285323

286324
// Check if includes exist
287325
for path in vendor_package.include_from_upstream.clone() {
288-
if !PathBuf::from(extend_paths(&[path.clone()], dep_path)?[0].clone()).exists() {
326+
if !PathBuf::from(extend_paths(&[path.clone()], dep_path, true)?[0].clone()).exists() {
289327
warnln!("{} not found in upstream, continuing.", path);
290328
}
291329
}
@@ -295,7 +333,7 @@ pub fn init(
295333
true => copy_recursively(
296334
&link_from,
297335
&link_to,
298-
&extend_paths(&vendor_package.include_from_upstream, dep_path)?,
336+
&extend_paths(&vendor_package.include_from_upstream, dep_path, false)?,
299337
&vendor_package
300338
.exclude_from_upstream
301339
.clone()
@@ -364,24 +402,32 @@ pub fn apply_patches(
364402
})
365403
.and_then(|_| {
366404
git.spawn_with(|c| {
367-
let current_patch_target = if !patch_link
405+
let is_file = patch_link
368406
.from_prefix
369407
.clone()
370408
.prefix_paths(git.path)
371409
.unwrap()
372-
.is_file()
373-
{
374-
patch_link.from_prefix.as_path()
410+
.is_file();
411+
412+
let current_patch_target = if is_file {
413+
patch_link.from_prefix.parent().unwrap().to_str().unwrap()
375414
} else {
376-
patch_link.from_prefix.parent().unwrap()
377-
}
378-
.to_str()
379-
.unwrap();
415+
patch_link.from_prefix.as_path().to_str().unwrap()
416+
};
417+
380418
c.arg("apply")
381419
.arg("--directory")
382420
.arg(current_patch_target)
383421
.arg("-p1")
384-
.arg(&patch)
422+
.arg(&patch);
423+
424+
// limit to specific file for file links
425+
if is_file {
426+
let file_path = patch_link.from_prefix.to_str().unwrap();
427+
c.arg("--include").arg(file_path);
428+
}
429+
430+
c
385431
})
386432
})
387433
.await
@@ -428,6 +474,7 @@ pub fn diff(
428474
&extend_paths(
429475
&vendor_package.include_from_upstream,
430476
&vendor_package.target_dir,
477+
false,
431478
)?,
432479
&vendor_package
433480
.exclude_from_upstream
@@ -559,30 +606,47 @@ pub fn gen_format_patch(
559606
// We assume that patch_dir matches Some() was checked outside this function.
560607
let patch_dir = patch_link.patch_dir.clone().unwrap();
561608

609+
// If the patch link maps a file, we operate in the file's parent directory
610+
// Therefore, only get the diff for that file.
611+
let include_pathspec = if !to_path.is_dir() {
612+
patch_link
613+
.to_prefix
614+
.file_name()
615+
.unwrap()
616+
.to_str()
617+
.unwrap()
618+
.to_string()
619+
} else {
620+
".".to_string()
621+
};
622+
623+
// Build the exclude pathspec to diff only the applicable files
624+
let exclude_pathspecs: Vec<String> = patch_link
625+
.exclude
626+
.iter()
627+
.map(|path| format!(":!{}", path.to_str().unwrap()))
628+
.collect();
629+
630+
let mut diff_args = vec![
631+
"diff".to_string(),
632+
"--relative".to_string(),
633+
"--cached".to_string(),
634+
];
635+
636+
diff_args.push(include_pathspec);
637+
for exclude_path in exclude_pathspecs {
638+
diff_args.push(exclude_path);
639+
}
640+
562641
// Get staged changes in dependency
563642
let get_diff_cached = rt
564-
.block_on(async {
565-
git_parent
566-
.spawn_with(|c| {
567-
c.arg("diff")
568-
.arg("--relative")
569-
.arg("--cached")
570-
.arg(if !to_path.is_dir() {
571-
// If the patch link maps a file, we operate in the file's parent
572-
// directory. Therefore, only get the diff for that file.
573-
patch_link.to_prefix.file_name().unwrap().to_str().unwrap()
574-
} else {
575-
"."
576-
})
577-
})
578-
.await
579-
})
643+
.block_on(async { git_parent.spawn_with(|c| c.args(&diff_args)).await })
580644
.map_err(|cause| Error::chain("Failed to generate diff", cause))?;
581645

582646
if !get_diff_cached.is_empty() {
583647
// Write diff into new temp dir. TODO: pipe directly to "git apply"
584648
let tmp_format_dir = TempDir::new()?;
585-
let tmp_format_path = tmp_format_dir.path();
649+
let tmp_format_path = tmp_format_dir.into_path();
586650
let diff_cached_path = tmp_format_path.join("staged.diff");
587651
std::fs::write(diff_cached_path.clone(), get_diff_cached)?;
588652

@@ -729,12 +793,13 @@ pub fn copy_recursively(
729793
pub fn extend_paths(
730794
include_from_upstream: &[String],
731795
prefix: impl AsRef<Path>,
796+
dir_only: bool,
732797
) -> Result<Vec<String>> {
733798
include_from_upstream
734799
.iter()
735800
.map(|pattern| {
736801
let pattern_long = PathBuf::from(pattern).prefix_paths(prefix.as_ref())?;
737-
if pattern_long.is_dir() {
802+
if pattern_long.is_dir() && !dir_only {
738803
Ok(String::from(pattern_long.join("**").to_str().unwrap()))
739804
} else {
740805
Ok(String::from(pattern_long.to_str().unwrap()))

src/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ impl Validate for PartialVendorPackage {
892892
},
893893
include_from_upstream: match self.include_from_upstream {
894894
Some(include_from_upstream) => include_from_upstream,
895-
None => vec![String::from("**")],
895+
None => vec![String::from("")],
896896
},
897897
exclude_from_upstream: {
898898
let mut excl = match self.exclude_from_upstream {

0 commit comments

Comments
 (0)