5 use std
::os
::raw
::c_int
;
8 use {raw, Oid, Repository, Error, FetchOptions}
;
9 use build
::CheckoutBuilder
;
10 use util
::{self, Binding}
;
12 /// A structure to represent a git [submodule][1]
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
>,
20 impl<'repo
> Submodule
<'repo
> {
21 /// Get the submodule's branch.
23 /// Returns `None` if the branch is not valid utf-8 or if the branch is not
25 pub fn branch(&self) -> Option
<&str> {
26 self.branch_bytes().and_then(|s
| str::from_utf8(s
).ok())
29 /// Get the branch for the submodule.
31 /// Returns `None` if the branch is not yet available.
32 pub fn branch_bytes(&self) -> Option
<&[u8]> {
34 ::opt_bytes(self, raw
::git_submodule_branch(self.raw
))
38 /// Get the submodule's url.
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() }
43 /// Get the url for the submodule.
44 pub fn url_bytes(&self) -> &[u8] {
46 ::opt_bytes(self, raw
::git_submodule_url(self.raw
)).unwrap()
50 /// Get the submodule's name.
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() }
55 /// Get the name for the submodule.
56 pub fn name_bytes(&self) -> &[u8] {
58 ::opt_bytes(self, raw
::git_submodule_name(self.raw
)).unwrap()
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()
69 /// Get the OID for the submodule in the current HEAD tree.
70 pub fn head_id(&self) -> Option
<Oid
> {
72 Binding
::from_raw_opt(raw
::git_submodule_head_id(self.raw
))
76 /// Get the OID for the submodule in the index.
77 pub fn index_id(&self) -> Option
<Oid
> {
79 Binding
::from_raw_opt(raw
::git_submodule_index_id(self.raw
))
83 /// Get the OID for the submodule in the current working directory.
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
> {
90 Binding
::from_raw_opt(raw
::git_submodule_wd_id(self.raw
))
94 /// Copy submodule info into ".git/config" file.
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.
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
> {
105 try_call
!(raw
::git_submodule_init(self.raw
, overwrite
));
110 /// Open the repository for a submodule.
112 /// This will only work if the submodule is checked out into the working
114 pub fn open(&self) -> Result
<Repository
, Error
> {
115 let mut raw
= ptr
::null_mut();
117 try_call
!(raw
::git_submodule_open(&mut raw
, self.raw
));
118 Ok(Binding
::from_raw(raw
))
122 /// Reread submodule info from config, index, and HEAD.
124 /// Call this to reread cached submodule information for this submodule if
125 /// you have reason to believe that it has changed.
127 /// If `force` is `true`, then data will be reloaded even if it doesn't seem
129 pub fn reload(&mut self, force
: bool
) -> Result
<(), Error
> {
131 try_call
!(raw
::git_submodule_reload(self.raw
, force
));
136 /// Copy submodule remote info into submodule repo.
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)); }
147 /// Add current submodule HEAD commit to index of superproject.
149 /// If `write_index` is true, then the index file will be immediately
150 /// written. Otherwise you must explicitly call `write()` on an `Index`
152 pub fn add_to_index(&mut self, write_index
: bool
) -> Result
<(), Error
> {
154 try_call
!(raw
::git_submodule_add_to_index(self.raw
, write_index
));
159 /// Resolve the setup of a new git submodule.
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)); }
170 /// Update submodule.
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`.
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
> {
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
)));
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 }
196 fn raw(&self) -> *mut raw
::git_submodule { self.raw }
199 impl<'repo
> Drop
for Submodule
<'repo
> {
201 unsafe { raw::git_submodule_free(self.raw) }
205 /// Options to update a submodule.
206 pub struct SubmoduleUpdateOptions
<'cb
> {
207 checkout_builder
: CheckoutBuilder
<'cb
>,
208 fetch_opts
: FetchOptions
<'cb
>,
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(),
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
,
231 fetch_opts
: self.fetch_opts
.raw(),
232 allow_fetch
: self.allow_fetch
as c_int
,
237 /// Set checkout options.
238 pub fn checkout(&mut self, opts
: CheckoutBuilder
<'cb
>) -> &mut Self {
239 self.checkout_builder
= opts
;
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;
250 /// Allow or disallow fetching.
251 pub fn allow_fetch(&mut self, b
: bool
) -> &mut Self {
252 self.allow_fetch
= b
;
257 impl<'cb
> Default
for SubmoduleUpdateOptions
<'cb
> {
258 fn default() -> Self {
267 use tempdir
::TempDir
;
271 use SubmoduleUpdateOptions
;
275 let td
= TempDir
::new("test").unwrap();
276 let repo
= Repository
::init(td
.path()).unwrap();
277 let mut s1
= repo
.submodule("/path/to/nowhere",
278 Path
::new("foo"), true).unwrap();
279 s1
.init(false).unwrap();
282 let s2
= repo
.submodule("/path/to/nowhere",
283 Path
::new("bar"), true).unwrap();
286 let mut submodules
= repo
.submodules().unwrap();
287 assert_eq
!(submodules
.len(), 2);
288 let mut s
= submodules
.remove(0);
289 assert_eq
!(s
.name(), Some("bar"));
290 assert_eq
!(s
.url(), Some("/path/to/nowhere"));
291 assert_eq
!(s
.branch(), None
);
292 assert
!(s
.head_id().is_none());
293 assert
!(s
.index_id().is_none());
294 assert
!(s
.workdir_id().is_none());
296 repo
.find_submodule("bar").unwrap();
298 assert
!(s
.path() == Path
::new("bar"));
299 s
.reload(true).unwrap();
303 fn add_a_submodule() {
304 let (_td
, repo1
) = ::test
::repo_init();
305 let (td
, repo2
) = ::test
::repo_init();
307 let url
= Url
::from_file_path(&repo1
.workdir().unwrap()).unwrap();
308 let mut s
= repo2
.submodule(&url
.to_string(), Path
::new("bar"),
310 t
!(fs
::remove_dir_all(td
.path().join("bar")));
311 t
!(Repository
::clone(&url
.to_string(),
312 td
.path().join("bar")));
313 t
!(s
.add_to_index(false));
314 t
!(s
.add_finalize());
318 fn update_submodule() {
319 // -----------------------------------
320 // Same as `add_a_submodule()`
321 let (_td
, repo1
) = ::test
::repo_init();
322 let (td
, repo2
) = ::test
::repo_init();
324 let url
= Url
::from_file_path(&repo1
.workdir().unwrap()).unwrap();
325 let mut s
= repo2
.submodule(&url
.to_string(), Path
::new("bar"),
327 t
!(fs
::remove_dir_all(td
.path().join("bar")));
328 t
!(Repository
::clone(&url
.to_string(),
329 td
.path().join("bar")));
330 t
!(s
.add_to_index(false));
331 t
!(s
.add_finalize());
332 // -----------------------------------
334 // Attempt to update submodule
335 let submodules
= t
!(repo1
.submodules());
336 for mut submodule
in submodules
{
337 let mut submodule_options
= SubmoduleUpdateOptions
::new();
339 let opts
= Some(&mut submodule_options
);
341 t
!(submodule
.update(init
, opts
));