Commit 077bed18 authored by Ben Boeckel's avatar Ben Boeckel

prepare: support checking out paths into the work tree

parent b72bdf00
Pipeline #43724 passed with stage
......@@ -100,6 +100,78 @@ lazy_static! {
Regex::new(r"^submodule\.(?P<name>.*)\.(?P<key>[^=]*)=(?P<value>.*)$").unwrap();
}
trait WorkareaGitContext {
fn cmd(&self) -> Command;
}
fn checkout<P>(ctx: &WorkareaGitContext, paths: &[P]) -> Result<()>
where P: AsRef<OsStr>,
{
// Checkout .gitmodules so that submodules work.
let ls_files = try!(ctx.cmd()
.arg("ls-files")
.arg("--")
.args(paths)
.output()
.chain_err(|| "failed to construct ls-files command"));
if !ls_files.status.success() {
bail!(ErrorKind::Git(format!("listing paths in the index: {}",
String::from_utf8_lossy(&ls_files.stderr))));
}
let mut checkout_index = try!(ctx.cmd()
.arg("checkout-index")
.arg("-f")
.arg("-q")
.arg("--stdin")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.chain_err(|| "failed to construct checkout-index command"));
try!(checkout_index.stdin
.as_mut()
.unwrap()
.write_all(&ls_files.stdout)
.chain_err(|| ErrorKind::Git("writing to checkout-index".to_string())));
let res = checkout_index.wait().unwrap();
if !res.success() {
let mut stderr = String::new();
try!(checkout_index.stderr
.as_mut()
.unwrap()
.read_to_string(&mut stderr)
.chain_err(|| "failed to read from checkout-index"));
bail!(ErrorKind::Git(format!("running checkout-index: {}", stderr)));
}
// Update the index for the files we put into the context
let mut update_index = try!(ctx.cmd()
.arg("update-index")
.arg("--stdin")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.chain_err(|| "failed to construct update-index command"));
try!(update_index.stdin
.as_mut()
.unwrap()
.write_all(&ls_files.stdout)
.chain_err(|| ErrorKind::Git("writing to update-index".to_string())));
let res = update_index.wait().unwrap();
if !res.success() {
let mut stderr = String::new();
try!(update_index.stderr
.as_mut()
.unwrap()
.read_to_string(&mut stderr)
.chain_err(|| "failed to read from update-index"));
bail!(ErrorKind::Git(format!("running update-index: {}", stderr)));
}
Ok(())
}
impl PreparingGitWorkArea {
// Create an area for performing actions which require a work tree.
fn new(context: GitContext, rev: &CommitId) -> Result<Self> {
......@@ -155,68 +227,7 @@ impl PreparingGitWorkArea {
// Explicitly do not check the return code; it is a failure.
// Checkout .gitmodules so that submodules work.
let ls_files = try!(self.git()
.arg("ls-files")
.arg("--")
.arg(".gitmodules")
.output()
.chain_err(|| "failed to construct ls-files command for .gitmodules"));
if !ls_files.status.success() {
bail!(ErrorKind::Git(format!("listing .gitmodules files in the index: {}",
String::from_utf8_lossy(&ls_files.stderr))));
}
let mut checkout_index = try!(self.git()
.arg("checkout-index")
.arg("-f")
.arg("-q")
.arg("--stdin")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.chain_err(|| "failed to construct checkout-index command"));
try!(checkout_index.stdin
.as_mut()
.unwrap()
.write_all(&ls_files.stdout)
.chain_err(|| ErrorKind::Git("writing to checkout-index".to_string())));
let res = checkout_index.wait().unwrap();
if !res.success() {
let mut stderr = String::new();
try!(checkout_index.stderr
.as_mut()
.unwrap()
.read_to_string(&mut stderr)
.chain_err(|| "failed to read from checkout-index"));
bail!(ErrorKind::Git(format!("running checkout-index for .gitmodules: {}", stderr)));
}
// Update the index for the files we put into the context
let mut update_index = try!(self.git()
.arg("update-index")
.arg("--stdin")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.chain_err(|| "failed to construct update-index command"));
try!(update_index.stdin
.as_mut()
.unwrap()
.write_all(&ls_files.stdout)
.chain_err(|| ErrorKind::Git("writing to update-index".to_string())));
let res = update_index.wait().unwrap();
if !res.success() {
let mut stderr = String::new();
try!(update_index.stderr
.as_mut()
.unwrap()
.read_to_string(&mut stderr)
.chain_err(|| "failed to read from update-index"));
bail!(ErrorKind::Git(format!("running update-index for .gitmodules: {}", stderr)));
}
Ok(())
checkout(self, &[".gitmodules"])
}
// Run a git command in the work area.
......@@ -281,6 +292,12 @@ impl PreparingGitWorkArea {
}
}
impl WorkareaGitContext for PreparingGitWorkArea {
fn cmd(&self) -> Command {
self.git()
}
}
impl PreparedGitWorkArea {
/// Create an area for performing actions which require a work tree.
pub fn new(context: GitContext, rev: &CommitId) -> Result<Self> {
......@@ -493,6 +510,19 @@ impl PreparedGitWorkArea {
Ok(conflict_info)
}
/// Checkout paths from the index to the filesystem.
///
/// Normally, files are not placed into the worktree, so checks which use other tools to
/// inspect file contents do not work. This method checks out files to the working directory
/// and fixes up Git's knowledge that they are there.
///
/// All paths supported by Git's globbing and searching mechanisms are supported.
pub fn checkout<P>(&self, paths: &[P]) -> Result<()>
where P: AsRef<OsStr>,
{
checkout(self, paths)
}
/// Prepare a command to create a merge commit.
///
/// The merge is performed, but only as a tree object. In order to create the actual commit
......@@ -578,4 +608,15 @@ impl PreparedGitWorkArea {
pub fn submodule_config(&self) -> &SubmoduleConfig {
&self.submodule_config
}
#[cfg(test)]
pub fn __work_tree(&self) -> PathBuf {
self.work_tree()
}
}
impl WorkareaGitContext for PreparedGitWorkArea {
fn cmd(&self) -> Command {
self.git()
}
}
......@@ -705,3 +705,30 @@ fn test_setup_merge_submodule_not_present() {
status);
}
}
#[test]
fn test_work_area_checkout() {
let tempdir = test_workspace_dir("test_work_area_checkout");
let base = CommitId::new(ARBITRARY_COMMIT);
let ctx = git_context(tempdir.path());
let workarea = ctx.prepare(&base).unwrap();
assert!(workarea.submodule_config().is_empty());
assert!(!workarea.__work_tree().join("rustfmt.toml").exists());
workarea.checkout(&["rustfmt.toml"]).unwrap();
assert!(workarea.__work_tree().join("rustfmt.toml").exists());
assert!(!workarea.__work_tree().join("Cargo.toml").exists());
workarea.checkout(&["*.toml"]).unwrap();
assert!(workarea.__work_tree().join("Cargo.toml").exists());
assert!(!workarea.__work_tree().join("src").exists());
workarea.checkout(&["src/"]).unwrap();
assert!(workarea.__work_tree().join("src").exists());
assert!(workarea.__work_tree().join("src/lib.rs").exists());
assert!(!workarea.__work_tree().join("README.md").exists());
workarea.checkout(&["*.md", "does-not-exist"]).unwrap();
assert!(workarea.__work_tree().join("README.md").exists());
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment