forked from rust-lang/cargo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit.rs
168 lines (150 loc) · 4.59 KB
/
git.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use cargo::util::ProcessError;
use git2;
use url::Url;
use support::{project, Project, ProjectBuilder, path2url};
#[must_use]
pub struct RepoBuilder {
repo: git2::Repository,
files: Vec<PathBuf>,
}
pub struct Repository(git2::Repository);
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,
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) -> Repository {
{
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, &[]));
}
let RepoBuilder { repo, .. } = self;
Repository(repo)
}
}
impl Repository {
pub fn root(&self) -> &Path {
self.0.workdir().unwrap()
}
pub fn url(&self) -> Url {
path2url(self.0.workdir().unwrap().to_path_buf())
}
pub fn revparse_head(&self) -> String {
self.0.revparse_single("HEAD").expect("revparse HEAD").id().to_string()
}
}
pub fn new<F>(name: &str, callback: F) -> Result<Project, ProcessError>
where
F: FnOnce(ProjectBuilder) -> ProjectBuilder,
{
let mut git_project = project().at(name);
git_project = callback(git_project);
let 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::IndexAddOption::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());
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
));
}