1 //! Builder-pattern objects for configuration various git operations.
3 use libc
::{c_char, c_int, c_uint, c_void, size_t}
;
4 use std
::ffi
::{CStr, CString}
;
9 use crate::util
::{self, Binding}
;
10 use crate::{panic, raw, Error, FetchOptions, IntoCString, Oid, Repository, Tree}
;
11 use crate::{CheckoutNotificationType, DiffFile, FileMode, Remote}
;
13 /// A builder struct which is used to build configuration for cloning a new git
18 /// Cloning using SSH:
21 /// use git2::{Cred, Error, RemoteCallbacks};
23 /// use std::path::Path;
25 /// // Prepare callbacks.
26 /// let mut callbacks = RemoteCallbacks::new();
27 /// callbacks.credentials(|_url, username_from_url, _allowed_types| {
29 /// username_from_url.unwrap(),
31 /// Path::new(&format!("{}/.ssh/id_rsa", env::var("HOME").unwrap())),
36 /// // Prepare fetch options.
37 /// let mut fo = git2::FetchOptions::new();
38 /// fo.remote_callbacks(callbacks);
40 /// // Prepare builder.
41 /// let mut builder = git2::build::RepoBuilder::new();
42 /// builder.fetch_options(fo);
44 /// // Clone the project.
46 /// "git@github.com:rust-lang/git2-rs.git",
47 /// Path::new("/tmp/git2-rs"),
50 pub struct RepoBuilder
<'cb
> {
52 branch
: Option
<CString
>,
55 checkout
: Option
<CheckoutBuilder
<'cb
>>,
56 fetch_opts
: Option
<FetchOptions
<'cb
>>,
57 clone_local
: Option
<CloneLocal
>,
58 remote_create
: Option
<Box
<RemoteCreate
<'cb
>>>,
61 /// Type of callback passed to `RepoBuilder::remote_create`.
63 /// The second and third arguments are the remote's name and the remote's URL.
64 pub type RemoteCreate
<'cb
> =
65 dyn for<'a
> FnMut(&'a Repository
, &str, &str) -> Result
<Remote
<'a
>, Error
> + 'cb
;
67 /// A builder struct for git tree updates.
69 /// Paths passed to `remove` and `upsert` can be multi-component paths, i.e. they
70 /// may contain slashes.
72 /// This is a higher-level tree update facility. There is also [`TreeBuilder`]
73 /// which is lower-level (and operates only on one level of the tree at a time).
75 /// [`TreeBuilder`]: crate::TreeBuilder
76 pub struct TreeUpdateBuilder
{
77 updates
: Vec
<raw
::git_tree_update
>,
81 /// A builder struct for configuring checkouts of a repository.
82 pub struct CheckoutBuilder
<'cb
> {
83 their_label
: Option
<CString
>,
84 our_label
: Option
<CString
>,
85 ancestor_label
: Option
<CString
>,
86 target_dir
: Option
<CString
>,
88 path_ptrs
: Vec
<*const c_char
>,
89 file_perm
: Option
<i32>,
90 dir_perm
: Option
<i32>,
91 disable_filters
: bool
,
93 progress
: Option
<Box
<Progress
<'cb
>>>,
94 notify
: Option
<Box
<Notify
<'cb
>>>,
95 notify_flags
: CheckoutNotificationType
,
98 /// Checkout progress notification callback.
100 /// The first argument is the path for the notification, the next is the number
101 /// of completed steps so far, and the final is the total number of steps.
102 pub type Progress
<'a
> = dyn FnMut(Option
<&Path
>, usize, usize) + 'a
;
104 /// Checkout notifications callback.
106 /// The first argument is the notification type, the next is the path for the
107 /// the notification, followed by the baseline diff, target diff, and workdir diff.
109 /// The callback must return a bool specifying whether the checkout should
111 pub type Notify
<'a
> = dyn FnMut(
112 CheckoutNotificationType
,
114 Option
<DiffFile
<'_
>>,
115 Option
<DiffFile
<'_
>>,
116 Option
<DiffFile
<'_
>>,
120 impl<'cb
> Default
for RepoBuilder
<'cb
> {
121 fn default() -> Self {
126 /// Options that can be passed to `RepoBuilder::clone_local`.
127 #[derive(Clone, Copy)]
128 pub enum CloneLocal
{
129 /// Auto-detect (default)
131 /// Here libgit2 will bypass the git-aware transport for local paths, but
132 /// use a normal fetch for `file://` URLs.
133 Auto
= raw
::GIT_CLONE_LOCAL_AUTO
as isize,
135 /// Bypass the git-aware transport even for `file://` URLs.
136 Local
= raw
::GIT_CLONE_LOCAL
as isize,
138 /// Never bypass the git-aware transport
139 None
= raw
::GIT_CLONE_NO_LOCAL
as isize,
141 /// Bypass the git-aware transport, but don't try to use hardlinks.
142 NoLinks
= raw
::GIT_CLONE_LOCAL_NO_LINKS
as isize,
145 __Nonexhaustive
= 0xff,
148 impl<'cb
> RepoBuilder
<'cb
> {
149 /// Creates a new repository builder with all of the default configuration.
151 /// When ready, the `clone()` method can be used to clone a new repository
152 /// using this configuration.
153 pub fn new() -> RepoBuilder
<'cb
> {
167 /// Indicate whether the repository will be cloned as a bare repository or
169 pub fn bare(&mut self, bare
: bool
) -> &mut RepoBuilder
<'cb
> {
174 /// Specify the name of the branch to check out after the clone.
176 /// If not specified, the remote's default branch will be used.
177 pub fn branch(&mut self, branch
: &str) -> &mut RepoBuilder
<'cb
> {
178 self.branch
= Some(CString
::new(branch
).unwrap());
182 /// Configures options for bypassing the git-aware transport on clone.
184 /// Bypassing it means that instead of a fetch libgit2 will copy the object
185 /// database directory instead of figuring out what it needs, which is
186 /// faster. If possible, it will hardlink the files to save space.
187 pub fn clone_local(&mut self, clone_local
: CloneLocal
) -> &mut RepoBuilder
<'cb
> {
188 self.clone_local
= Some(clone_local
);
192 /// Set the flag for bypassing the git aware transport mechanism for local
195 /// If `true`, the git-aware transport will be bypassed for local paths. If
196 /// `false`, the git-aware transport will not be bypassed.
197 #[deprecated(note = "use `clone_local` instead")]
199 pub fn local(&mut self, local
: bool
) -> &mut RepoBuilder
<'cb
> {
204 /// Set the flag for whether hardlinks are used when using a local git-aware
205 /// transport mechanism.
206 #[deprecated(note = "use `clone_local` instead")]
208 pub fn hardlinks(&mut self, links
: bool
) -> &mut RepoBuilder
<'cb
> {
209 self.hardlinks
= links
;
213 /// Configure the checkout which will be performed by consuming a checkout
215 pub fn with_checkout(&mut self, checkout
: CheckoutBuilder
<'cb
>) -> &mut RepoBuilder
<'cb
> {
216 self.checkout
= Some(checkout
);
220 /// Options which control the fetch, including callbacks.
222 /// The callbacks are used for reporting fetch progress, and for acquiring
223 /// credentials in the event they are needed.
224 pub fn fetch_options(&mut self, fetch_opts
: FetchOptions
<'cb
>) -> &mut RepoBuilder
<'cb
> {
225 self.fetch_opts
= Some(fetch_opts
);
229 /// Configures a callback used to create the git remote, prior to its being
230 /// used to perform the clone operation.
231 pub fn remote_create
<F
>(&mut self, f
: F
) -> &mut RepoBuilder
<'cb
>
233 F
: for<'a
> FnMut(&'a Repository
, &str, &str) -> Result
<Remote
<'a
>, Error
> + 'cb
,
235 self.remote_create
= Some(Box
::new(f
));
239 /// Clone a remote repository.
241 /// This will use the options configured so far to clone the specified URL
242 /// into the specified local path.
243 pub fn clone(&mut self, url
: &str, into
: &Path
) -> Result
<Repository
, Error
> {
244 let mut opts
: raw
::git_clone_options
= unsafe { mem::zeroed() }
;
246 try_call
!(raw
::git_clone_init_options(
248 raw
::GIT_CLONE_OPTIONS_VERSION
251 opts
.bare
= self.bare
as c_int
;
252 opts
.checkout_branch
= self
256 .unwrap_or(ptr
::null());
258 if let Some(ref local
) = self.clone_local
{
259 opts
.local
= *local
as raw
::git_clone_local_t
;
261 opts
.local
= match (self.local
, self.hardlinks
) {
262 (true, false) => raw
::GIT_CLONE_LOCAL_NO_LINKS
,
263 (false, _
) => raw
::GIT_CLONE_NO_LOCAL
,
264 (true, _
) => raw
::GIT_CLONE_LOCAL_AUTO
,
268 if let Some(ref mut cbs
) = self.fetch_opts
{
269 opts
.fetch_opts
= cbs
.raw();
272 if let Some(ref mut c
) = self.checkout
{
274 c
.configure(&mut opts
.checkout_opts
);
278 if let Some(ref mut callback
) = self.remote_create
{
279 opts
.remote_cb
= Some(remote_create_cb
);
280 opts
.remote_cb_payload
= callback
as *mut _
as *mut _
;
283 let url
= CString
::new(url
)?
;
284 // Normal file path OK (does not need Windows conversion).
285 let into
= into
.into_c_string()?
;
286 let mut raw
= ptr
::null_mut();
288 try_call
!(raw
::git_clone(&mut raw
, url
, into
, &opts
));
289 Ok(Binding
::from_raw(raw
))
294 extern "C" fn remote_create_cb(
295 out
: *mut *mut raw
::git_remote
,
296 repo
: *mut raw
::git_repository
,
299 payload
: *mut c_void
,
302 let repo
= Repository
::from_raw(repo
);
303 let code
= panic
::wrap(|| {
304 let name
= CStr
::from_ptr(name
).to_str().unwrap();
305 let url
= CStr
::from_ptr(url
).to_str().unwrap();
306 let f
= payload
as *mut Box
<RemoteCreate
<'_
>>;
307 match (*f
)(&repo
, name
, url
) {
309 *out
= crate::remote
::remote_into_raw(remote
);
312 Err(e
) => e
.raw_code(),
320 impl<'cb
> Default
for CheckoutBuilder
<'cb
> {
321 fn default() -> Self {
326 impl<'cb
> CheckoutBuilder
<'cb
> {
327 /// Creates a new builder for checkouts with all of its default
329 pub fn new() -> CheckoutBuilder
<'cb
> {
332 disable_filters
: false,
335 path_ptrs
: Vec
::new(),
338 ancestor_label
: None
,
341 checkout_opts
: raw
::GIT_CHECKOUT_SAFE
as u32,
344 notify_flags
: CheckoutNotificationType
::empty(),
348 /// Indicate that this checkout should perform a dry run by checking for
349 /// conflicts but not make any actual changes.
350 pub fn dry_run(&mut self) -> &mut CheckoutBuilder
<'cb
> {
351 self.checkout_opts
&= !((1 << 4) - 1);
352 self.checkout_opts
|= raw
::GIT_CHECKOUT_NONE
as u32;
356 /// Take any action necessary to get the working directory to match the
357 /// target including potentially discarding modified files.
358 pub fn force(&mut self) -> &mut CheckoutBuilder
<'cb
> {
359 self.checkout_opts
&= !((1 << 4) - 1);
360 self.checkout_opts
|= raw
::GIT_CHECKOUT_FORCE
as u32;
364 /// Indicate that the checkout should be performed safely, allowing new
365 /// files to be created but not overwriting existing files or changes.
367 /// This is the default.
368 pub fn safe(&mut self) -> &mut CheckoutBuilder
<'cb
> {
369 self.checkout_opts
&= !((1 << 4) - 1);
370 self.checkout_opts
|= raw
::GIT_CHECKOUT_SAFE
as u32;
374 fn flag(&mut self, bit
: raw
::git_checkout_strategy_t
, on
: bool
) -> &mut CheckoutBuilder
<'cb
> {
376 self.checkout_opts
|= bit
as u32;
378 self.checkout_opts
&= !(bit
as u32);
383 /// In safe mode, create files that don't exist.
385 /// Defaults to false.
386 pub fn recreate_missing(&mut self, allow
: bool
) -> &mut CheckoutBuilder
<'cb
> {
387 self.flag(raw
::GIT_CHECKOUT_RECREATE_MISSING
, allow
)
390 /// In safe mode, apply safe file updates even when there are conflicts
391 /// instead of canceling the checkout.
393 /// Defaults to false.
394 pub fn allow_conflicts(&mut self, allow
: bool
) -> &mut CheckoutBuilder
<'cb
> {
395 self.flag(raw
::GIT_CHECKOUT_ALLOW_CONFLICTS
, allow
)
398 /// Remove untracked files from the working dir.
400 /// Defaults to false.
401 pub fn remove_untracked(&mut self, remove
: bool
) -> &mut CheckoutBuilder
<'cb
> {
402 self.flag(raw
::GIT_CHECKOUT_REMOVE_UNTRACKED
, remove
)
405 /// Remove ignored files from the working dir.
407 /// Defaults to false.
408 pub fn remove_ignored(&mut self, remove
: bool
) -> &mut CheckoutBuilder
<'cb
> {
409 self.flag(raw
::GIT_CHECKOUT_REMOVE_IGNORED
, remove
)
412 /// Only update the contents of files that already exist.
414 /// If set, files will not be created or deleted.
416 /// Defaults to false.
417 pub fn update_only(&mut self, update
: bool
) -> &mut CheckoutBuilder
<'cb
> {
418 self.flag(raw
::GIT_CHECKOUT_UPDATE_ONLY
, update
)
421 /// Prevents checkout from writing the updated files' information to the
424 /// Defaults to true.
425 pub fn update_index(&mut self, update
: bool
) -> &mut CheckoutBuilder
<'cb
> {
426 self.flag(raw
::GIT_CHECKOUT_DONT_UPDATE_INDEX
, !update
)
429 /// Indicate whether the index and git attributes should be refreshed from
430 /// disk before any operations.
432 /// Defaults to true,
433 pub fn refresh(&mut self, refresh
: bool
) -> &mut CheckoutBuilder
<'cb
> {
434 self.flag(raw
::GIT_CHECKOUT_NO_REFRESH
, !refresh
)
437 /// Skip files with unmerged index entries.
439 /// Defaults to false.
440 pub fn skip_unmerged(&mut self, skip
: bool
) -> &mut CheckoutBuilder
<'cb
> {
441 self.flag(raw
::GIT_CHECKOUT_SKIP_UNMERGED
, skip
)
444 /// Indicate whether the checkout should proceed on conflicts by using the
445 /// stage 2 version of the file ("ours").
447 /// Defaults to false.
448 pub fn use_ours(&mut self, ours
: bool
) -> &mut CheckoutBuilder
<'cb
> {
449 self.flag(raw
::GIT_CHECKOUT_USE_OURS
, ours
)
452 /// Indicate whether the checkout should proceed on conflicts by using the
453 /// stage 3 version of the file ("theirs").
455 /// Defaults to false.
456 pub fn use_theirs(&mut self, theirs
: bool
) -> &mut CheckoutBuilder
<'cb
> {
457 self.flag(raw
::GIT_CHECKOUT_USE_THEIRS
, theirs
)
460 /// Indicate whether ignored files should be overwritten during the checkout.
462 /// Defaults to true.
463 pub fn overwrite_ignored(&mut self, overwrite
: bool
) -> &mut CheckoutBuilder
<'cb
> {
464 self.flag(raw
::GIT_CHECKOUT_DONT_OVERWRITE_IGNORED
, !overwrite
)
467 /// Indicate whether a normal merge file should be written for conflicts.
469 /// Defaults to false.
470 pub fn conflict_style_merge(&mut self, on
: bool
) -> &mut CheckoutBuilder
<'cb
> {
471 self.flag(raw
::GIT_CHECKOUT_CONFLICT_STYLE_MERGE
, on
)
474 /// Specify for which notification types to invoke the notification
477 /// Defaults to none.
480 notification_types
: CheckoutNotificationType
,
481 ) -> &mut CheckoutBuilder
<'cb
> {
482 self.notify_flags
= notification_types
;
486 /// Indicates whether to include common ancestor data in diff3 format files
489 /// Defaults to false.
490 pub fn conflict_style_diff3(&mut self, on
: bool
) -> &mut CheckoutBuilder
<'cb
> {
491 self.flag(raw
::GIT_CHECKOUT_CONFLICT_STYLE_DIFF3
, on
)
494 /// Indicate whether to apply filters like CRLF conversion.
495 pub fn disable_filters(&mut self, disable
: bool
) -> &mut CheckoutBuilder
<'cb
> {
496 self.disable_filters
= disable
;
500 /// Set the mode with which new directories are created.
503 pub fn dir_perm(&mut self, perm
: i32) -> &mut CheckoutBuilder
<'cb
> {
504 self.dir_perm
= Some(perm
);
508 /// Set the mode with which new files are created.
510 /// The default is 0644 or 0755 as dictated by the blob.
511 pub fn file_perm(&mut self, perm
: i32) -> &mut CheckoutBuilder
<'cb
> {
512 self.file_perm
= Some(perm
);
516 /// Add a path to be checked out.
518 /// If no paths are specified, then all files are checked out. Otherwise
519 /// only these specified paths are checked out.
520 pub fn path
<T
: IntoCString
>(&mut self, path
: T
) -> &mut CheckoutBuilder
<'cb
> {
521 let path
= util
::cstring_to_repo_path(path
).unwrap();
522 self.path_ptrs
.push(path
.as_ptr());
523 self.paths
.push(path
);
527 /// Set the directory to check out to
528 pub fn target_dir(&mut self, dst
: &Path
) -> &mut CheckoutBuilder
<'cb
> {
529 // Normal file path OK (does not need Windows conversion).
530 self.target_dir
= Some(dst
.into_c_string().unwrap());
534 /// The name of the common ancestor side of conflicts
535 pub fn ancestor_label(&mut self, label
: &str) -> &mut CheckoutBuilder
<'cb
> {
536 self.ancestor_label
= Some(CString
::new(label
).unwrap());
540 /// The name of the common our side of conflicts
541 pub fn our_label(&mut self, label
: &str) -> &mut CheckoutBuilder
<'cb
> {
542 self.our_label
= Some(CString
::new(label
).unwrap());
546 /// The name of the common their side of conflicts
547 pub fn their_label(&mut self, label
: &str) -> &mut CheckoutBuilder
<'cb
> {
548 self.their_label
= Some(CString
::new(label
).unwrap());
552 /// Set a callback to receive notifications of checkout progress.
553 pub fn progress
<F
>(&mut self, cb
: F
) -> &mut CheckoutBuilder
<'cb
>
555 F
: FnMut(Option
<&Path
>, usize, usize) + 'cb
,
557 self.progress
= Some(Box
::new(cb
) as Box
<Progress
<'cb
>>);
561 /// Set a callback to receive checkout notifications.
563 /// Callbacks are invoked prior to modifying any files on disk.
564 /// Returning `false` from the callback will cancel the checkout.
565 pub fn notify
<F
>(&mut self, cb
: F
) -> &mut CheckoutBuilder
<'cb
>
568 CheckoutNotificationType
,
570 Option
<DiffFile
<'_
>>,
571 Option
<DiffFile
<'_
>>,
572 Option
<DiffFile
<'_
>>,
576 self.notify
= Some(Box
::new(cb
) as Box
<Notify
<'cb
>>);
580 /// Configure a raw checkout options based on this configuration.
582 /// This method is unsafe as there is no guarantee that this structure will
583 /// outlive the provided checkout options.
584 pub unsafe fn configure(&mut self, opts
: &mut raw
::git_checkout_options
) {
585 opts
.version
= raw
::GIT_CHECKOUT_OPTIONS_VERSION
;
586 opts
.disable_filters
= self.disable_filters
as c_int
;
587 opts
.dir_mode
= self.dir_perm
.unwrap_or(0) as c_uint
;
588 opts
.file_mode
= self.file_perm
.unwrap_or(0) as c_uint
;
590 if !self.path_ptrs
.is_empty() {
591 opts
.paths
.strings
= self.path_ptrs
.as_ptr() as *mut _
;
592 opts
.paths
.count
= self.path_ptrs
.len() as size_t
;
595 if let Some(ref c
) = self.target_dir
{
596 opts
.target_directory
= c
.as_ptr();
598 if let Some(ref c
) = self.ancestor_label
{
599 opts
.ancestor_label
= c
.as_ptr();
601 if let Some(ref c
) = self.our_label
{
602 opts
.our_label
= c
.as_ptr();
604 if let Some(ref c
) = self.their_label
{
605 opts
.their_label
= c
.as_ptr();
607 if self.progress
.is_some() {
608 opts
.progress_cb
= Some(progress_cb
);
609 opts
.progress_payload
= self as *mut _
as *mut _
;
611 if self.notify
.is_some() {
612 opts
.notify_cb
= Some(notify_cb
);
613 opts
.notify_payload
= self as *mut _
as *mut _
;
614 opts
.notify_flags
= self.notify_flags
.bits() as c_uint
;
616 opts
.checkout_strategy
= self.checkout_opts
as c_uint
;
620 extern "C" fn progress_cb(
626 panic
::wrap(|| unsafe {
627 let payload
= &mut *(data
as *mut CheckoutBuilder
<'_
>);
628 let callback
= match payload
.progress
{
629 Some(ref mut c
) => c
,
632 let path
= if path
.is_null() {
635 Some(util
::bytes2path(CStr
::from_ptr(path
).to_bytes()))
637 callback(path
, completed
as usize, total
as usize)
641 extern "C" fn notify_cb(
642 why
: raw
::git_checkout_notify_t
,
644 baseline
: *const raw
::git_diff_file
,
645 target
: *const raw
::git_diff_file
,
646 workdir
: *const raw
::git_diff_file
,
650 panic
::wrap(|| unsafe {
651 let payload
= &mut *(data
as *mut CheckoutBuilder
<'_
>);
652 let callback
= match payload
.notify
{
653 Some(ref mut c
) => c
,
656 let path
= if path
.is_null() {
659 Some(util
::bytes2path(CStr
::from_ptr(path
).to_bytes()))
662 let baseline
= if baseline
.is_null() {
665 Some(DiffFile
::from_raw(baseline
))
668 let target
= if target
.is_null() {
671 Some(DiffFile
::from_raw(target
))
674 let workdir
= if workdir
.is_null() {
677 Some(DiffFile
::from_raw(workdir
))
680 let why
= CheckoutNotificationType
::from_bits_truncate(why
as u32);
681 let keep_going
= callback(why
, path
, baseline
, target
, workdir
);
691 unsafe impl Send
for TreeUpdateBuilder {}
693 impl Default
for TreeUpdateBuilder
{
694 fn default() -> Self {
699 impl TreeUpdateBuilder
{
700 /// Create a new empty series of updates.
701 pub fn new() -> Self {
708 /// Add an update removing the specified `path` from a tree.
709 pub fn remove
<T
: IntoCString
>(&mut self, path
: T
) -> &mut Self {
710 let path
= util
::cstring_to_repo_path(path
).unwrap();
711 let path_ptr
= path
.as_ptr();
712 self.paths
.push(path
);
713 self.updates
.push(raw
::git_tree_update
{
714 action
: raw
::GIT_TREE_UPDATE_REMOVE
,
716 id
: [0; raw
::GIT_OID_RAWSZ
],
718 filemode
: raw
::GIT_FILEMODE_UNREADABLE
,
724 /// Add an update setting the specified `path` to a specific Oid, whether it currently exists
727 /// Note that libgit2 does not support an upsert of a previously removed path, or an upsert
728 /// that changes the type of an object (such as from tree to blob or vice versa).
729 pub fn upsert
<T
: IntoCString
>(&mut self, path
: T
, id
: Oid
, filemode
: FileMode
) -> &mut Self {
730 let path
= util
::cstring_to_repo_path(path
).unwrap();
731 let path_ptr
= path
.as_ptr();
732 self.paths
.push(path
);
733 self.updates
.push(raw
::git_tree_update
{
734 action
: raw
::GIT_TREE_UPDATE_UPSERT
,
735 id
: unsafe { *id.raw() }
,
736 filemode
: u32::from(filemode
) as raw
::git_filemode_t
,
742 /// Create a new tree from the specified baseline and this series of updates.
744 /// The baseline tree must exist in the specified repository.
745 pub fn create_updated(&mut self, repo
: &Repository
, baseline
: &Tree
<'_
>) -> Result
<Oid
, Error
> {
746 let mut ret
= raw
::git_oid
{
747 id
: [0; raw
::GIT_OID_RAWSZ
],
750 try_call
!(raw
::git_tree_create_updated(
755 self.updates
.as_ptr()
757 Ok(Binding
::from_raw(&ret
as *const _
))
764 use super::{CheckoutBuilder, RepoBuilder, TreeUpdateBuilder}
;
765 use crate::{CheckoutNotificationType, FileMode, Repository}
;
768 use tempfile
::TempDir
;
772 let r
= RepoBuilder
::new().clone("/path/to/nowhere", Path
::new("foo"));
778 let td
= TempDir
::new().unwrap();
779 Repository
::init_bare(&td
.path().join("bare")).unwrap();
780 let url
= if cfg
!(unix
) {
781 format
!("file://{}/bare", td
.path().display())
785 td
.path().display().to_string().replace("\\", "/")
789 let dst
= td
.path().join("foo");
790 RepoBuilder
::new().clone(&url
, &dst
).unwrap();
791 fs
::remove_dir_all(&dst
).unwrap();
792 assert
!(RepoBuilder
::new().branch("foo").clone(&url
, &dst
).is_err());
796 fn smoke_tree_create_updated() {
797 let (_tempdir
, repo
) = crate::test
::repo_init();
798 let (_
, tree_id
) = crate::test
::commit(&repo
);
799 let tree
= t
!(repo
.find_tree(tree_id
));
800 assert
!(tree
.get_name("bar").is_none());
801 let foo_id
= tree
.get_name("foo").unwrap().id();
802 let tree2_id
= t
!(TreeUpdateBuilder
::new()
804 .upsert("bar/baz", foo_id
, FileMode
::Blob
)
805 .create_updated(&repo
, &tree
));
806 let tree2
= t
!(repo
.find_tree(tree2_id
));
807 assert
!(tree2
.get_name("foo").is_none());
808 let baz_id
= tree2
.get_path(Path
::new("bar/baz")).unwrap().id();
809 assert_eq
!(foo_id
, baz_id
);
812 /// Issue regression test #365
814 fn notify_callback() {
815 let td
= TempDir
::new().unwrap();
816 let cd
= TempDir
::new().unwrap();
819 let mut opts
= crate::RepositoryInitOptions
::new();
820 opts
.initial_head("main");
821 let repo
= Repository
::init_opts(&td
.path(), &opts
).unwrap();
823 let mut config
= repo
.config().unwrap();
824 config
.set_str("user.name", "name").unwrap();
825 config
.set_str("user.email", "email").unwrap();
827 let mut index
= repo
.index().unwrap();
828 let p
= Path
::new(td
.path()).join("file");
829 println
!("using path {:?}", p
);
830 fs
::File
::create(&p
).unwrap();
831 index
.add_path(&Path
::new("file")).unwrap();
832 let id
= index
.write_tree().unwrap();
834 let tree
= repo
.find_tree(id
).unwrap();
835 let sig
= repo
.signature().unwrap();
836 repo
.commit(Some("HEAD"), &sig
, &sig
, "initial", &tree
, &[])
840 let repo
= Repository
::open_bare(&td
.path().join(".git")).unwrap();
842 .revparse_single(&"main")
846 let mut index
= repo
.index().unwrap();
847 index
.read_tree(&tree
).unwrap();
849 let mut checkout_opts
= CheckoutBuilder
::new();
850 checkout_opts
.target_dir(&cd
.path());
851 checkout_opts
.notify_on(CheckoutNotificationType
::all());
852 checkout_opts
.notify(|_notif
, _path
, baseline
, target
, workdir
| {
853 assert
!(baseline
.is_none());
854 assert_eq
!(target
.unwrap().path(), Some(Path
::new("file")));
855 assert
!(workdir
.is_none());
858 repo
.checkout_index(Some(&mut index
), Some(&mut checkout_opts
))