Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: allow patching Git source dependencies with patch files #9001

Closed
wants to merge 3 commits into from
Closed
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
Prev Previous commit
Apply patches specified for Git sources
Patches need to be of `git format-patch` export. Filenames are relative
to workspace manifest. For example:

    [patch.crates-io]
    time = { git = "https://github.com/time-rs/time", tag = "0.1.41", patch-files = ["patches/0001-Hack-around-a-getting-precise-time-in-Windows.patch"]}
  • Loading branch information
da-x committed Dec 19, 2020
commit b918bc6ca482fa06881a1dd16cc5b296f4ffd9c9
2 changes: 1 addition & 1 deletion src/cargo/sources/git/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub use self::source::GitSource;
pub use self::utils::{fetch, GitCheckout, GitDatabase, GitRemote};
mod source;
mod utils;
pub mod utils;
18 changes: 16 additions & 2 deletions src/cargo/sources/git/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl<'cfg> Source for GitSource<'cfg> {
self.source_id
}

fn update_ws<'a>(&mut self, _ws: Option<&Workspace<'a>>, _patch_files: &Vec<String>) -> CargoResult<()> {
fn update_ws<'a>(&mut self, ws: Option<&Workspace<'a>>, patch_files: &Vec<String>) -> CargoResult<()> {
let git_path = self.config.git_path();
let git_path = self.config.assert_package_cache_locked(&git_path);
let db_path = git_path.join("db").join(&self.ident);
Expand Down Expand Up @@ -168,10 +168,18 @@ impl<'cfg> Source for GitSource<'cfg> {
// <https://github.com/servo/servo/pull/14397>.
let short_id = db.to_short_id(actual_rev)?;

let base_path = if patch_files.is_empty() {
std::path::PathBuf::from(git_path)
} else if let Some(ws) = ws {
ws.target_dir().to_owned().as_path_unlocked().to_owned()
} else {
anyhow::bail!("not from workspace")
};

// Check out `actual_rev` from the database to a scoped location on the
// filesystem. This will use hard links and such to ideally make the
// checkout operation here pretty fast.
let checkout_path = git_path
let checkout_path = base_path
.join("checkouts")
.join(&self.ident)
.join(short_id.as_str());
Expand All @@ -180,6 +188,12 @@ impl<'cfg> Source for GitSource<'cfg> {
let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
let path_source = PathSource::new_recursive(&checkout_path, source_id, self.config);

if !patch_files.is_empty() {
if let Some(ws) = ws {
path_source.apply_patch_files(ws, actual_rev, &patch_files)?;
}
}

self.path_source = Some(path_source);
self.locked_rev = Some(actual_rev);
self.path_source.as_mut().unwrap().update()
Expand Down
55 changes: 42 additions & 13 deletions src/cargo/sources/git/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
//! That's the dream at least, we'll see how this plays out.

use crate::core::GitReference;
use crate::util::{ProcessBuilder};
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::paths;
use crate::util::process_builder::process;
Expand Down Expand Up @@ -971,6 +972,20 @@ pub fn fetch(
})
}

fn fix_git_env(cmd: &mut ProcessBuilder) {
// If cargo is run by git (for example, the `exec` command in `git
// rebase`), the GIT_DIR is set by git and will point to the wrong
// location (this takes precedence over the cwd). Make sure this is
// unset so git will look at cwd for the repo.
cmd.env_remove("GIT_DIR");
// The reset of these may not be necessary, but I'm including them
// just to be extra paranoid and avoid any issues.
cmd.env_remove("GIT_WORK_TREE");
cmd.env_remove("GIT_INDEX_FILE");
cmd.env_remove("GIT_OBJECT_DIRECTORY");
cmd.env_remove("GIT_ALTERNATE_OBJECT_DIRECTORIES");
}

fn fetch_with_cli(
repo: &mut git2::Repository,
url: &str,
Expand All @@ -979,33 +994,47 @@ fn fetch_with_cli(
config: &Config,
) -> CargoResult<()> {
let mut cmd = process("git");
fix_git_env(&mut cmd);
cmd.cwd(repo.path());
cmd.arg("fetch");
if tags {
cmd.arg("--tags");
}
cmd.arg("--force") // handle force pushes
.arg("--update-head-ok") // see discussion in #2078
.arg(url)
.args(refspecs)
// If cargo is run by git (for example, the `exec` command in `git
// rebase`), the GIT_DIR is set by git and will point to the wrong
// location (this takes precedence over the cwd). Make sure this is
// unset so git will look at cwd for the repo.
.env_remove("GIT_DIR")
// The reset of these may not be necessary, but I'm including them
// just to be extra paranoid and avoid any issues.
.env_remove("GIT_WORK_TREE")
.env_remove("GIT_INDEX_FILE")
.env_remove("GIT_OBJECT_DIRECTORY")
.env_remove("GIT_ALTERNATE_OBJECT_DIRECTORIES")
.cwd(repo.path());
.args(refspecs);
config
.shell()
.verbose(|s| s.status("Running", &cmd.to_string()))?;
cmd.exec_with_output()?;
Ok(())
}

pub fn apply_patch(
repo: &mut git2::Repository,
path: &Path,
config: &Config,
) -> CargoResult<()> {
let mut cmd = process("git");
fix_git_env(&mut cmd);
if let Some(workdir) = repo.workdir() {
cmd.cwd(workdir);
} else {
anyhow::bail!("not working directory");
}
cmd.arg("-c");
cmd.arg("user.email=automatic.applier@no-email.com");
cmd.arg("-c");
cmd.arg("user.name=Automatic applier");
cmd.arg("am");
cmd.arg(&path);

config.shell().verbose(|s| s.status("Patching", &cmd.to_string()))?;
cmd.exec_with_output()?;
Ok(())
}

/// Cargo has a bunch of long-lived git repositories in its global cache and
/// some, like the index, are updated very frequently. Right now each update
/// creates a new "pack file" inside the git database, and over time this can
Expand Down
26 changes: 26 additions & 0 deletions src/cargo/sources/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ impl<'cfg> PathSource<'cfg> {
}
}

/// Apply patches on path source. They will be kept as workdir changes.
pub fn apply_patch_files(&self, ws: &Workspace<'cfg>, base_rev: git2::Oid, patch_files: &Vec<String>) -> CargoResult<()> {
let mut repo = if let Ok(repo) = git2::Repository::open(&self.path) {
repo
} else {
anyhow::bail!("Cannot open git repo {}", self.path.display());
};

// Start with the revision base
let obj = repo.find_object(base_rev, None).unwrap();
repo.reset(&obj, git2::ResetType::Hard, None)?;
drop(obj);

// Use the Git machinary to apply working tree changes
for patch in patch_files {
let path = ws.root().join(patch);
super::git::utils::apply_patch(&mut repo, &path, self.config)?;
}

// Reset HEAD back to the base revision
let obj = repo.find_object(base_rev, None).unwrap();
repo.reset(&obj, git2::ResetType::Soft, None)?;

Ok(())
}

/// Creates a new source which is walked recursively to discover packages.
///
/// This is similar to the `new` method except that instead of requiring a
Expand Down