]> git.proxmox.com Git - cargo.git/blob - vendor/git2-0.6.8/src/submodule.rs
New upstream version 0.23.0
[cargo.git] / vendor / git2-0.6.8 / src / submodule.rs
1 use std::marker;
2 use std::mem;
3 use std::ptr;
4 use std::str;
5 use std::os::raw::c_int;
6 use std::path::Path;
7
8 use {raw, Oid, Repository, Error, FetchOptions};
9 use build::CheckoutBuilder;
10 use util::{self, Binding};
11
12 /// A structure to represent a git [submodule][1]
13 ///
14 /// [1]: http://git-scm.com/book/en/Git-Tools-Submodules
15 pub struct Submodule<'repo> {
16 raw: *mut raw::git_submodule,
17 _marker: marker::PhantomData<&'repo Repository>,
18 }
19
20 impl<'repo> Submodule<'repo> {
21 /// Get the submodule's branch.
22 ///
23 /// Returns `None` if the branch is not valid utf-8 or if the branch is not
24 /// yet available.
25 pub fn branch(&self) -> Option<&str> {
26 self.branch_bytes().and_then(|s| str::from_utf8(s).ok())
27 }
28
29 /// Get the branch for the submodule.
30 ///
31 /// Returns `None` if the branch is not yet available.
32 pub fn branch_bytes(&self) -> Option<&[u8]> {
33 unsafe {
34 ::opt_bytes(self, raw::git_submodule_branch(self.raw))
35 }
36 }
37
38 /// Get the submodule's url.
39 ///
40 /// Returns `None` if the url is not valid utf-8
41 pub fn url(&self) -> Option<&str> { str::from_utf8(self.url_bytes()).ok() }
42
43 /// Get the url for the submodule.
44 pub fn url_bytes(&self) -> &[u8] {
45 unsafe {
46 ::opt_bytes(self, raw::git_submodule_url(self.raw)).unwrap()
47 }
48 }
49
50 /// Get the submodule's name.
51 ///
52 /// Returns `None` if the name is not valid utf-8
53 pub fn name(&self) -> Option<&str> { str::from_utf8(self.name_bytes()).ok() }
54
55 /// Get the name for the submodule.
56 pub fn name_bytes(&self) -> &[u8] {
57 unsafe {
58 ::opt_bytes(self, raw::git_submodule_name(self.raw)).unwrap()
59 }
60 }
61
62 /// Get the path for the submodule.
63 pub fn path(&self) -> &Path {
64 util::bytes2path(unsafe {
65 ::opt_bytes(self, raw::git_submodule_path(self.raw)).unwrap()
66 })
67 }
68
69 /// Get the OID for the submodule in the current HEAD tree.
70 pub fn head_id(&self) -> Option<Oid> {
71 unsafe {
72 Binding::from_raw_opt(raw::git_submodule_head_id(self.raw))
73 }
74 }
75
76 /// Get the OID for the submodule in the index.
77 pub fn index_id(&self) -> Option<Oid> {
78 unsafe {
79 Binding::from_raw_opt(raw::git_submodule_index_id(self.raw))
80 }
81 }
82
83 /// Get the OID for the submodule in the current working directory.
84 ///
85 /// This returns the OID that corresponds to looking up 'HEAD' in the
86 /// checked out submodule. If there are pending changes in the index or
87 /// anything else, this won't notice that.
88 pub fn workdir_id(&self) -> Option<Oid> {
89 unsafe {
90 Binding::from_raw_opt(raw::git_submodule_wd_id(self.raw))
91 }
92 }
93
94 /// Copy submodule info into ".git/config" file.
95 ///
96 /// Just like "git submodule init", this copies information about the
97 /// submodule into ".git/config". You can use the accessor functions above
98 /// to alter the in-memory git_submodule object and control what is written
99 /// to the config, overriding what is in .gitmodules.
100 ///
101 /// By default, existing entries will not be overwritten, but passing `true`
102 /// for `overwrite` forces them to be updated.
103 pub fn init(&mut self, overwrite: bool) -> Result<(), Error> {
104 unsafe {
105 try_call!(raw::git_submodule_init(self.raw, overwrite));
106 }
107 Ok(())
108 }
109
110 /// Open the repository for a submodule.
111 ///
112 /// This will only work if the submodule is checked out into the working
113 /// directory.
114 pub fn open(&self) -> Result<Repository, Error> {
115 let mut raw = ptr::null_mut();
116 unsafe {
117 try_call!(raw::git_submodule_open(&mut raw, self.raw));
118 Ok(Binding::from_raw(raw))
119 }
120 }
121
122 /// Reread submodule info from config, index, and HEAD.
123 ///
124 /// Call this to reread cached submodule information for this submodule if
125 /// you have reason to believe that it has changed.
126 ///
127 /// If `force` is `true`, then data will be reloaded even if it doesn't seem
128 /// out of date
129 pub fn reload(&mut self, force: bool) -> Result<(), Error> {
130 unsafe {
131 try_call!(raw::git_submodule_reload(self.raw, force));
132 }
133 Ok(())
134 }
135
136 /// Copy submodule remote info into submodule repo.
137 ///
138 /// This copies the information about the submodules URL into the checked
139 /// out submodule config, acting like "git submodule sync". This is useful
140 /// if you have altered the URL for the submodule (or it has been altered
141 /// by a fetch of upstream changes) and you need to update your local repo.
142 pub fn sync(&mut self) -> Result<(), Error> {
143 unsafe { try_call!(raw::git_submodule_sync(self.raw)); }
144 Ok(())
145 }
146
147 /// Add current submodule HEAD commit to index of superproject.
148 ///
149 /// If `write_index` is true, then the index file will be immediately
150 /// written. Otherwise you must explicitly call `write()` on an `Index`
151 /// later on.
152 pub fn add_to_index(&mut self, write_index: bool) -> Result<(), Error> {
153 unsafe {
154 try_call!(raw::git_submodule_add_to_index(self.raw, write_index));
155 }
156 Ok(())
157 }
158
159 /// Resolve the setup of a new git submodule.
160 ///
161 /// This should be called on a submodule once you have called add setup and
162 /// done the clone of the submodule. This adds the .gitmodules file and the
163 /// newly cloned submodule to the index to be ready to be committed (but
164 /// doesn't actually do the commit).
165 pub fn add_finalize(&mut self) -> Result<(), Error> {
166 unsafe { try_call!(raw::git_submodule_add_finalize(self.raw)); }
167 Ok(())
168 }
169
170 /// Update submodule.
171 ///
172 /// This will clone a missing submodule and check out the subrepository to
173 /// the commit specified in the index of the containing repository. If
174 /// the submodule repository doesn't contain the target commit, then the
175 /// submodule is fetched using the fetch options supplied in `opts`.
176 ///
177 /// `init` indicates if the submodule should be initialized first if it has
178 /// not been initialized yet.
179 pub fn update(&mut self, init: bool,
180 opts: Option<&mut SubmoduleUpdateOptions>)
181 -> Result<(), Error> {
182 unsafe {
183 let mut raw_opts = opts.map(|o| o.raw());
184 try_call!(raw::git_submodule_update(self.raw, init as c_int,
185 raw_opts.as_mut().map_or(ptr::null_mut(), |o| o)));
186 }
187 Ok(())
188 }
189 }
190
191 impl<'repo> Binding for Submodule<'repo> {
192 type Raw = *mut raw::git_submodule;
193 unsafe fn from_raw(raw: *mut raw::git_submodule) -> Submodule<'repo> {
194 Submodule { raw: raw, _marker: marker::PhantomData }
195 }
196 fn raw(&self) -> *mut raw::git_submodule { self.raw }
197 }
198
199 impl<'repo> Drop for Submodule<'repo> {
200 fn drop(&mut self) {
201 unsafe { raw::git_submodule_free(self.raw) }
202 }
203 }
204
205 /// Options to update a submodule.
206 pub struct SubmoduleUpdateOptions<'cb> {
207 checkout_builder: CheckoutBuilder<'cb>,
208 fetch_opts: FetchOptions<'cb>,
209 allow_fetch: bool,
210 }
211
212 impl<'cb> SubmoduleUpdateOptions<'cb> {
213 /// Return default options.
214 pub fn new() -> Self {
215 SubmoduleUpdateOptions {
216 checkout_builder: CheckoutBuilder::new(),
217 fetch_opts: FetchOptions::new(),
218 allow_fetch: true,
219 }
220 }
221
222 unsafe fn raw(&mut self) -> raw::git_submodule_update_options {
223 let mut checkout_opts: raw::git_checkout_options = mem::zeroed();
224 let init_res = raw::git_checkout_init_options(&mut checkout_opts,
225 raw::GIT_CHECKOUT_OPTIONS_VERSION);
226 assert_eq!(0, init_res);
227 self.checkout_builder.configure(&mut checkout_opts);
228 let opts = raw::git_submodule_update_options {
229 version: raw::GIT_SUBMODULE_UPDATE_OPTIONS_VERSION,
230 checkout_opts,
231 fetch_opts: self.fetch_opts.raw(),
232 allow_fetch: self.allow_fetch as c_int,
233 };
234 opts
235 }
236
237 /// Set checkout options.
238 pub fn checkout(&mut self, opts: CheckoutBuilder<'cb>) -> &mut Self {
239 self.checkout_builder = opts;
240 self
241 }
242
243 /// Set fetch options and allow fetching.
244 pub fn fetch(&mut self, opts: FetchOptions<'cb>) -> &mut Self {
245 self.fetch_opts = opts;
246 self.allow_fetch = true;
247 self
248 }
249
250 /// Allow or disallow fetching.
251 pub fn allow_fetch(&mut self, b: bool) -> &mut Self {
252 self.allow_fetch = b;
253 self
254 }
255 }
256
257 impl<'cb> Default for SubmoduleUpdateOptions<'cb> {
258 fn default() -> Self {
259 Self::new()
260 }
261 }
262
263 #[cfg(test)]
264 mod tests {
265 use std::path::Path;
266 use std::fs;
267 use tempdir::TempDir;
268 use url::Url;
269
270 use Repository;
271
272 #[test]
273 fn smoke() {
274 let td = TempDir::new("test").unwrap();
275 let repo = Repository::init(td.path()).unwrap();
276 let mut s1 = repo.submodule("/path/to/nowhere",
277 Path::new("foo"), true).unwrap();
278 s1.init(false).unwrap();
279 s1.sync().unwrap();
280
281 let s2 = repo.submodule("/path/to/nowhere",
282 Path::new("bar"), true).unwrap();
283 drop((s1, s2));
284
285 let mut submodules = repo.submodules().unwrap();
286 assert_eq!(submodules.len(), 2);
287 let mut s = submodules.remove(0);
288 assert_eq!(s.name(), Some("bar"));
289 assert_eq!(s.url(), Some("/path/to/nowhere"));
290 assert_eq!(s.branch(), None);
291 assert!(s.head_id().is_none());
292 assert!(s.index_id().is_none());
293 assert!(s.workdir_id().is_none());
294
295 repo.find_submodule("bar").unwrap();
296 s.open().unwrap();
297 assert!(s.path() == Path::new("bar"));
298 s.reload(true).unwrap();
299 }
300
301 #[test]
302 fn add_a_submodule() {
303 let (_td, repo1) = ::test::repo_init();
304 let (td, repo2) = ::test::repo_init();
305
306 let url = Url::from_file_path(&repo1.workdir().unwrap()).unwrap();
307 let mut s = repo2.submodule(&url.to_string(), Path::new("bar"),
308 true).unwrap();
309 t!(fs::remove_dir_all(td.path().join("bar")));
310 t!(Repository::clone(&url.to_string(),
311 td.path().join("bar")));
312 t!(s.add_to_index(false));
313 t!(s.add_finalize());
314 }
315 }