mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Git now fetches DBs and clones repos from them
This commit is contained in:
parent
6afe63320b
commit
be9d1cde34
@ -8,14 +8,16 @@ extern crate url;
|
|||||||
use hammer::FlagConfig;
|
use hammer::FlagConfig;
|
||||||
use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult};
|
use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult};
|
||||||
use cargo::util::ToCLI;
|
use cargo::util::ToCLI;
|
||||||
use cargo::sources::git::{GitCommand,GitRepo};
|
use cargo::sources::git::{GitRemoteRepo,GitRepo};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[deriving(Eq,Clone,Decodable)]
|
#[deriving(Eq,Clone,Decodable)]
|
||||||
struct Options {
|
struct Options {
|
||||||
directory: String,
|
database_path: String,
|
||||||
|
checkout_path: String,
|
||||||
url: String,
|
url: String,
|
||||||
reference: String
|
reference: String,
|
||||||
|
verbose: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlagConfig for Options {}
|
impl FlagConfig for Options {}
|
||||||
@ -25,9 +27,17 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn execute(options: Options) -> CLIResult<Option<GitRepo>> {
|
fn execute(options: Options) -> CLIResult<Option<GitRepo>> {
|
||||||
let url: Url = try!(from_str(options.url.as_slice()).to_result(|_|
|
let Options { database_path, checkout_path, url, reference, verbose } = options;
|
||||||
CLIError::new(format!("The URL `{}` you passed was not a valid URL", options.url), None::<&str>, 1)));
|
|
||||||
|
|
||||||
let cmd = GitCommand::new(Path::new(options.directory.clone()), url, options.reference);
|
let url: Url = try!(from_str(url.as_slice()).to_result(|_|
|
||||||
cmd.checkout().to_cli(1).map(|repo| Some(repo))
|
CLIError::new(format!("The URL `{}` you passed was not a valid URL", url), None::<&str>, 1)));
|
||||||
|
|
||||||
|
let repo = GitRemoteRepo::new(Path::new(database_path), url, reference, verbose);
|
||||||
|
let local = try!(repo.checkout().map_err(|e|
|
||||||
|
CLIError::new(format!("Couldn't check out repository: {}", e), None::<&str>, 1)));
|
||||||
|
|
||||||
|
try!(local.copy_to(Path::new(checkout_path)).map_err(|e|
|
||||||
|
CLIError::new(format!("Couldn't copy repository: {}", e), None::<&str>, 1)));
|
||||||
|
|
||||||
|
Ok(Some(local))
|
||||||
}
|
}
|
||||||
|
@ -9,30 +9,41 @@ use std::io::fs::{mkdir_recursive,rmdir_recursive,chmod};
|
|||||||
use serialize::{Encodable,Encoder};
|
use serialize::{Encodable,Encoder};
|
||||||
|
|
||||||
macro_rules! git(
|
macro_rules! git(
|
||||||
($config:expr, $str:expr, $($rest:expr),*) => (
|
($config:expr, $verbose:expr, $str:expr, $($rest:expr),*) => (
|
||||||
try!(git_inherit(&$config, format!($str, $($rest),*)))
|
try!(git_inherit(&$config, $verbose, format!($str, $($rest),*)))
|
||||||
);
|
);
|
||||||
|
|
||||||
($config:expr, $str:expr) => (
|
($config:expr, $verbose:expr, $str:expr) => (
|
||||||
try!(git_inherit(&$config, format!($str)))
|
try!(git_inherit(&$config, $verbose, format!($str)))
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
|
|
||||||
macro_rules! git_output(
|
macro_rules! git_output(
|
||||||
($config:expr, $str:expr, $($rest:expr),*) => (
|
($config:expr, $verbose:expr, $str:expr, $($rest:expr),*) => (
|
||||||
try!(git_output(&$config, format!($str, $($rest),*)))
|
try!(git_output(&$config, $verbose, format!($str, $($rest),*)))
|
||||||
);
|
);
|
||||||
|
|
||||||
($config:expr, $str:expr) => (
|
($config:expr, $verbose:expr, $str:expr) => (
|
||||||
try!(git_output(&$config, format!($str)))
|
try!(git_output(&$config, $verbose, format!($str)))
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
|
|
||||||
|
macro_rules! errln(
|
||||||
|
($($arg:tt)*) => (let _ = writeln!(::std::io::stdio::stderr(), $($arg)*))
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitConfig represents the information about a git location for code determined from
|
||||||
|
* a Cargo manifest, as well as a location to store the git database for a remote
|
||||||
|
* repository.
|
||||||
|
*/
|
||||||
|
|
||||||
#[deriving(Eq,Clone)]
|
#[deriving(Eq,Clone)]
|
||||||
struct GitConfig {
|
struct GitConfig {
|
||||||
path: Path,
|
path: Path,
|
||||||
uri: Url,
|
uri: Url,
|
||||||
reference: String
|
reference: String,
|
||||||
|
verbose: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Eq,Clone,Encodable)]
|
#[deriving(Eq,Clone,Encodable)]
|
||||||
@ -42,6 +53,42 @@ struct EncodableGitConfig {
|
|||||||
reference: String
|
reference: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitRemoteRepo is responsible for taking a GitConfig and bringing the local database up
|
||||||
|
* to date with the remote repository, returning a GitRepo.
|
||||||
|
*
|
||||||
|
* A GitRemoteRepo has a `reference` in its config, which may not resolve to a valid revision.
|
||||||
|
* Its `checkout` method returns a `GitRepo` which is guaranteed to have a resolved
|
||||||
|
* revision for the supplied reference.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[deriving(Eq,Clone)]
|
||||||
|
pub struct GitRemoteRepo {
|
||||||
|
config: GitConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitRepo is a local clone of a remote repository's database. The supplied reference is
|
||||||
|
* guaranteed to resolve to a valid `revision`, so all code run from this point forward
|
||||||
|
* can assume that the requested code exists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[deriving(Eq,Clone,Encodable)]
|
||||||
|
pub struct GitRepo {
|
||||||
|
config: GitConfig,
|
||||||
|
revision: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitCheckout is a local checkout of a particular revision. A single GitRepo can
|
||||||
|
* have multiple GitCheckouts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub struct GitCheckout<'a> {
|
||||||
|
location: Path,
|
||||||
|
repo: &'a GitRepo
|
||||||
|
}
|
||||||
|
|
||||||
impl<E, S: Encoder<E>> Encodable<S, E> for GitConfig {
|
impl<E, S: Encoder<E>> Encodable<S, E> for GitConfig {
|
||||||
fn encode(&self, s: &mut S) -> Result<(), E> {
|
fn encode(&self, s: &mut S) -> Result<(), E> {
|
||||||
EncodableGitConfig {
|
EncodableGitConfig {
|
||||||
@ -52,20 +99,9 @@ impl<E, S: Encoder<E>> Encodable<S, E> for GitConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Eq,Clone)]
|
impl GitRemoteRepo {
|
||||||
pub struct GitCommand {
|
pub fn new(path: Path, uri: Url, reference: String, verbose: bool) -> GitRemoteRepo {
|
||||||
config: GitConfig
|
GitRemoteRepo { config: GitConfig { path: path, uri: uri, reference: reference, verbose: verbose } }
|
||||||
}
|
|
||||||
|
|
||||||
#[deriving(Eq,Clone,Encodable)]
|
|
||||||
pub struct GitRepo {
|
|
||||||
config: GitConfig,
|
|
||||||
revision: String
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GitCommand {
|
|
||||||
pub fn new(path: Path, uri: Url, reference: String) -> GitCommand {
|
|
||||||
GitCommand { config: GitConfig { path: path, uri: uri, reference: reference } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cwd<'a>(&'a self) -> &'a Path {
|
pub fn get_cwd<'a>(&'a self) -> &'a Path {
|
||||||
@ -84,7 +120,7 @@ impl GitCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self) -> CargoResult<()> {
|
fn fetch(&self) -> CargoResult<()> {
|
||||||
Ok(git!(self.config.path, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", self.config.uri))
|
Ok(git!(self.config.path, self.config.verbose, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", self.config.uri))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&self) -> CargoResult<()> {
|
fn clone(&self) -> CargoResult<()> {
|
||||||
@ -93,13 +129,24 @@ impl GitCommand {
|
|||||||
try!(mkdir_recursive(&self.config.path, UserDir).map_err(|err|
|
try!(mkdir_recursive(&self.config.path, UserDir).map_err(|err|
|
||||||
human_error(format!("Couldn't recursively create `{}`", dirname.display()), format!("path={}", dirname.display()), io_error(err))));
|
human_error(format!("Couldn't recursively create `{}`", dirname.display()), format!("path={}", dirname.display()), io_error(err))));
|
||||||
|
|
||||||
Ok(git!(dirname, "clone {} {} --bare --no-hardlinks --quiet", self.config.uri, self.config.path.display()))
|
Ok(git!(dirname, self.config.verbose, "clone {} {} --bare --no-hardlinks --quiet", self.config.uri, self.config.path.display()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GitCheckout<'a> {
|
impl GitRepo {
|
||||||
location: Path,
|
fn get_path<'a>(&'a self) -> &'a Path {
|
||||||
repo: &'a GitRepo
|
&self.config.path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_to<'a>(&'a self, dest: Path) -> CargoResult<GitCheckout<'a>> {
|
||||||
|
let checkout = try!(GitCheckout::clone(dest, self));
|
||||||
|
|
||||||
|
try!(checkout.fetch());
|
||||||
|
try!(checkout.reset(self.revision.as_slice()));
|
||||||
|
try!(checkout.update_submodules());
|
||||||
|
|
||||||
|
Ok(checkout)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GitCheckout<'a> {
|
impl<'a> GitCheckout<'a> {
|
||||||
@ -118,83 +165,67 @@ impl<'a> GitCheckout<'a> {
|
|||||||
self.repo.get_path()
|
self.repo.get_path()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_repo(&self) -> CargoResult<()> {
|
fn get_verbose(&self) -> bool {
|
||||||
try!(mkdir_recursive(&Path::new(self.location.dirname()), UserDir).map_err(io_error));
|
self.repo.config.verbose
|
||||||
try!(rmdir_recursive(&self.location).map_err(io_error));
|
}
|
||||||
|
|
||||||
git!(self.location, "clone --no-checkout --quiet {} {}", self.get_source().display(), self.location.display());
|
fn clone_repo(&self) -> CargoResult<()> {
|
||||||
|
let dirname = Path::new(self.location.dirname());
|
||||||
|
|
||||||
|
try!(mkdir_recursive(&dirname, UserDir).map_err(|e|
|
||||||
|
human_error(format!("Couldn't mkdir {}", Path::new(self.location.dirname()).display()), None::<&str>, io_error(e))));
|
||||||
|
|
||||||
|
if self.location.exists() {
|
||||||
|
try!(rmdir_recursive(&self.location).map_err(|e|
|
||||||
|
human_error(format!("Couldn't rmdir {}", Path::new(&self.location).display()), None::<&str>, io_error(e))));
|
||||||
|
}
|
||||||
|
|
||||||
|
git!(dirname, self.get_verbose(), "clone --no-checkout --quiet {} {}", self.get_source().display(), self.location.display());
|
||||||
try!(chmod(&self.location, AllPermissions).map_err(io_error));
|
try!(chmod(&self.location, AllPermissions).map_err(io_error));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self) -> CargoResult<()> {
|
fn fetch(&self) -> CargoResult<()> {
|
||||||
Ok(git!(self.location, "fetch --force --quiet --tags {}", self.get_source().display()))
|
Ok(git!(self.location, self.get_verbose(), "fetch --force --quiet --tags {}", self.get_source().display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset<T: Show>(&self, revision: T) -> CargoResult<()> {
|
fn reset<T: Show>(&self, revision: T) -> CargoResult<()> {
|
||||||
Ok(git!(self.location, "reset --hard {}", revision))
|
Ok(git!(self.location, self.get_verbose(), "reset -q --hard {}", revision))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_submodules(&self) -> CargoResult<()> {
|
fn update_submodules(&self) -> CargoResult<()> {
|
||||||
Ok(git!(self.location, "submodule update --init --recursive"))
|
Ok(git!(self.location, self.get_verbose(), "submodule update --init --recursive --quiet"))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GitRepo {
|
|
||||||
fn get_path<'a>(&'a self) -> &'a Path {
|
|
||||||
&self.config.path
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_variable)]
|
|
||||||
fn copy_to<'a>(&'a self, dest: Path) -> CargoResult<GitCheckout<'a>> {
|
|
||||||
let checkout = try!(GitCheckout::clone(dest, self));
|
|
||||||
|
|
||||||
try!(checkout.fetch());
|
|
||||||
try!(checkout.reset(self.revision.as_slice()));
|
|
||||||
try!(checkout.update_submodules());
|
|
||||||
|
|
||||||
Ok(checkout)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_to(&self, destination: &Path) -> CargoResult<()> {
|
|
||||||
try!(mkdir_recursive(&Path::new(destination.dirname()), UserDir).map_err(io_error));
|
|
||||||
try!(rmdir_recursive(destination).map_err(io_error));
|
|
||||||
git!(self.config.path, "clone --no-checkout --quiet {} {}", self.config.path.display(), destination.display());
|
|
||||||
try!(chmod(destination, AllPermissions).map_err(io_error));
|
|
||||||
|
|
||||||
git!(*destination, "fetch --force --quiet --tags {}", self.config.path.display());
|
|
||||||
git!(*destination, "reset --hard {}", self.revision);
|
|
||||||
git!(*destination, "submodule update --init --recursive");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_for(config: &GitConfig) -> CargoResult<String> {
|
fn rev_for(config: &GitConfig) -> CargoResult<String> {
|
||||||
Ok(git_output!(config.path, "rev-parse {}", config.reference))
|
Ok(git_output!(config.path, config.verbose, "rev-parse {}", config.reference))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn has_rev<T: Show>(path: &Path, rev: T) -> bool {
|
fn has_rev<T: Show>(path: &Path, rev: T) -> bool {
|
||||||
git_output(path, format!("cat-file -e {}", rev)).is_ok()
|
git_output(path, false, format!("cat-file -e {}", rev)).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn git(path: &Path, str: &str) -> ProcessBuilder {
|
fn git(path: &Path, verbose: bool, str: &str) -> ProcessBuilder {
|
||||||
println!("Executing git {} @ {}", str, path.display());
|
if verbose {
|
||||||
|
errln!("Executing git {} @ {}", str, path.display());
|
||||||
|
}
|
||||||
|
|
||||||
process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice()).cwd(path.clone())
|
process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice()).cwd(path.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn git_inherit(path: &Path, str: String) -> CargoResult<()> {
|
fn git_inherit(path: &Path, verbose: bool, str: String) -> CargoResult<()> {
|
||||||
git(path, str.as_slice()).exec().map_err(|err|
|
git(path, verbose, str.as_slice()).exec().map_err(|err|
|
||||||
human_error(format!("Couldn't execute `git {}`: {}", str, err), None::<&str>, err))
|
human_error(format!("Couldn't execute `git {}`: {}", str, err), None::<&str>, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn git_output(path: &Path, str: String) -> CargoResult<String> {
|
fn git_output(path: &Path, verbose: bool, str: String) -> CargoResult<String> {
|
||||||
let output = try!(git(path, str.as_slice()).exec_with_output().map_err(|err|
|
let output = try!(git(path, verbose, str.as_slice()).exec_with_output().map_err(|err|
|
||||||
human_error(format!("Couldn't execute `git {}`", str), None::<&str>, err)));
|
human_error(format!("Couldn't execute `git {}`", str), None::<&str>, err)));
|
||||||
|
|
||||||
Ok(to_str(output.output.as_slice()))
|
Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_str(vec: &[u8]) -> String {
|
fn to_str(vec: &[u8]) -> String {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user