blob: 393157a62b16e33f9476cf5408def9a2ecb0baba [file] [log] [blame]
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use url::Url;
use git2;
use cargo::util::ProcessError;
use support::{ProjectBuilder, project, path2url};
pub struct RepoBuilder {
repo: git2::Repository,
files: Vec<PathBuf>,
}
pub fn repo(p: &Path) -> RepoBuilder { RepoBuilder::init(p) }
impl RepoBuilder {
pub fn init(p: &Path) -> RepoBuilder {
t!(fs::create_dir_all(p.parent().unwrap()));
let repo = t!(git2::Repository::init(p));
{
let mut config = t!(repo.config());
t!(config.set_str("user.name", "name"));
t!(config.set_str("user.email", "email"));
}
RepoBuilder { repo: repo, files: Vec::new() }
}
pub fn file(self, path: &str, contents: &str) -> RepoBuilder {
let mut me = self.nocommit_file(path, contents);
me.files.push(PathBuf::from(path));
me
}
pub fn nocommit_file(self, path: &str, contents: &str) -> RepoBuilder {
let dst = self.repo.workdir().unwrap().join(path);
t!(fs::create_dir_all(dst.parent().unwrap()));
t!(t!(File::create(&dst)).write_all(contents.as_bytes()));
self
}
pub fn build(&self) {
let mut index = t!(self.repo.index());
for file in self.files.iter() {
t!(index.add_path(file));
}
t!(index.write());
let id = t!(index.write_tree());
let tree = t!(self.repo.find_tree(id));
let sig = t!(self.repo.signature());
t!(self.repo.commit(Some("HEAD"), &sig, &sig,
"Initial commit", &tree, &[]));
}
pub fn root(&self) -> &Path {
self.repo.workdir().unwrap()
}
pub fn url(&self) -> Url {
path2url(self.repo.workdir().unwrap().to_path_buf())
}
}
pub fn new<F>(name: &str, callback: F) -> Result<ProjectBuilder, ProcessError>
where F: FnOnce(ProjectBuilder) -> ProjectBuilder
{
let mut git_project = project(name);
git_project = callback(git_project);
git_project.build();
let repo = t!(git2::Repository::init(&git_project.root()));
let mut cfg = t!(repo.config());
t!(cfg.set_str("user.email", "foo@bar.com"));
t!(cfg.set_str("user.name", "Foo Bar"));
drop(cfg);
add(&repo);
commit(&repo);
Ok(git_project)
}
pub fn add(repo: &git2::Repository) {
// FIXME(libgit2/libgit2#2514): apparently add_all will add all submodules
// as well, and then fail b/c they're a directory. As a stopgap, we just
// ignore all submodules.
let mut s = t!(repo.submodules());
for submodule in s.iter_mut() {
t!(submodule.add_to_index(false));
}
let mut index = t!(repo.index());
t!(index.add_all(["*"].iter(), git2::ADD_DEFAULT,
Some(&mut (|a, _b| {
if s.iter().any(|s| a.starts_with(s.path())) {1} else {0}
}))));
t!(index.write());
}
pub fn add_submodule<'a>(repo: &'a git2::Repository, url: &str,
path: &Path) -> git2::Submodule<'a>
{
let path = path.to_str().unwrap().replace(r"\", "/");
let mut s = t!(repo.submodule(url, Path::new(&path), false));
let subrepo = t!(s.open());
t!(subrepo.remote_add_fetch("origin", "refs/heads/*:refs/heads/*"));
let mut origin = t!(subrepo.find_remote("origin"));
t!(origin.fetch(&[], None, None));
t!(subrepo.checkout_head(None));
t!(s.add_finalize());
return s;
}
pub fn commit(repo: &git2::Repository) -> git2::Oid {
let tree_id = t!(t!(repo.index()).write_tree());
let sig = t!(repo.signature());
let mut parents = Vec::new();
match repo.head().ok().map(|h| h.target().unwrap()) {
Some(parent) => parents.push(t!(repo.find_commit(parent))),
None => {}
}
let parents = parents.iter().collect::<Vec<_>>();
t!(repo.commit(Some("HEAD"), &sig, &sig, "test",
&t!(repo.find_tree(tree_id)),
&parents))
}
pub fn tag(repo: &git2::Repository, name: &str) {
let head = repo.head().unwrap().target().unwrap();
t!(repo.tag(name,
&t!(repo.find_object(head, None)),
&t!(repo.signature()),
"make a new tag",
false));
}