2 use crate::reference
::Reference
;
3 use crate::repo
::Repository
;
4 use crate::util
::{self, Binding}
;
5 use crate::{raw, Error}
;
6 use std
::os
::raw
::c_int
;
10 use std
::{marker, mem}
;
12 /// An owned git worktree
14 /// This structure corresponds to a `git_worktree` in libgit2.
17 raw
: *mut raw
::git_worktree
,
20 /// Options which can be used to configure how a worktree is initialized
21 pub struct WorktreeAddOptions
<'a
> {
22 raw
: raw
::git_worktree_add_options
,
23 _marker
: marker
::PhantomData
<Reference
<'a
>>,
26 /// Options to configure how worktree pruning is performed
27 pub struct WorktreePruneOptions
{
28 raw
: raw
::git_worktree_prune_options
,
31 /// Lock Status of a worktree
32 #[derive(PartialEq, Debug)]
33 pub enum WorktreeLockStatus
{
34 /// Worktree is Unlocked
36 /// Worktree is locked with the optional message
37 Locked(Option
<String
>),
41 /// Open a worktree of a the repository
43 /// If a repository is not the main tree but a worktree, this
44 /// function will look up the worktree inside the parent
45 /// repository and create a new `git_worktree` structure.
46 pub fn open_from_repository(repo
: &Repository
) -> Result
<Worktree
, Error
> {
47 let mut raw
= ptr
::null_mut();
49 try_call
!(raw
::git_worktree_open_from_repository(&mut raw
, repo
.raw()));
50 Ok(Binding
::from_raw(raw
))
54 /// Retrieves the name of the worktree
56 /// This is the name that can be passed to repo::Repository::find_worktree
57 /// to reopen the worktree. This is also the name that would appear in the
58 /// list returned by repo::Repository::worktrees
59 pub fn name(&self) -> Option
<&str> {
61 crate::opt_bytes(self, raw
::git_worktree_name(self.raw
))
62 .and_then(|s
| str::from_utf8(s
).ok())
66 /// Retrieves the path to the worktree
68 /// This is the path to the top-level of the source and not the path to the
69 /// .git file within the worktree. This path can be passed to
70 /// repo::Repository::open.
71 pub fn path(&self) -> &Path
{
73 util
::bytes2path(crate::opt_bytes(self, raw
::git_worktree_path(self.raw
)).unwrap())
77 /// Validates the worktree
79 /// This checks that it still exists on the
80 /// filesystem and that the metadata is correct
81 pub fn validate(&self) -> Result
<(), Error
> {
83 try_call
!(raw
::git_worktree_validate(self.raw
));
88 /// Locks the worktree
89 pub fn lock(&self, reason
: Option
<&str>) -> Result
<(), Error
> {
90 let reason
= crate::opt_cstr(reason
)?
;
92 try_call
!(raw
::git_worktree_lock(self.raw
, reason
));
97 /// Unlocks the worktree
98 pub fn unlock(&self) -> Result
<(), Error
> {
100 try_call
!(raw
::git_worktree_unlock(self.raw
));
105 /// Checks if worktree is locked
106 pub fn is_locked(&self) -> Result
<WorktreeLockStatus
, Error
> {
107 let buf
= Buf
::new();
109 match try_call
!(raw
::git_worktree_is_locked(buf
.raw(), self.raw
)) {
110 0 => Ok(WorktreeLockStatus
::Unlocked
),
112 let v
= buf
.to_vec();
113 Ok(WorktreeLockStatus
::Locked(match v
.len() {
115 _
=> Some(String
::from_utf8(v
).unwrap()),
122 /// Prunes the worktree
123 pub fn prune(&self, opts
: Option
<&mut WorktreePruneOptions
>) -> Result
<(), Error
> {
124 // When successful the worktree should be removed however the backing structure
125 // of the git_worktree should still be valid.
127 try_call
!(raw
::git_worktree_prune(self.raw
, opts
.map(|o
| o
.raw())));
132 /// Checks if the worktree is prunable
133 pub fn is_prunable(&self, opts
: Option
<&mut WorktreePruneOptions
>) -> Result
<bool
, Error
> {
135 let rv
= try_call
!(raw
::git_worktree_is_prunable(
137 opts
.map(|o
| o
.raw())
144 impl<'a
> WorktreeAddOptions
<'a
> {
145 /// Creates a default set of add options.
147 /// By default this will not lock the worktree
148 pub fn new() -> WorktreeAddOptions
<'a
> {
150 let mut raw
= mem
::zeroed();
152 raw
::git_worktree_add_options_init(&mut raw
, raw
::GIT_WORKTREE_ADD_OPTIONS_VERSION
),
157 _marker
: marker
::PhantomData
,
162 /// If enabled, this will cause the newly added worktree to be locked
163 pub fn lock(&mut self, enabled
: bool
) -> &mut WorktreeAddOptions
<'a
> {
164 self.raw
.lock
= enabled
as c_int
;
168 /// reference to use for the new worktree HEAD
171 reference
: Option
<&'a Reference
<'_
>>,
172 ) -> &mut WorktreeAddOptions
<'a
> {
173 self.raw
.reference
= if let Some(reference
) = reference
{
181 /// Get a set of raw add options to be used with `git_worktree_add`
182 pub fn raw(&self) -> *const raw
::git_worktree_add_options
{
187 impl WorktreePruneOptions
{
188 /// Creates a default set of pruning options
190 /// By defaults this will prune only worktrees that are no longer valid
191 /// unlocked and not checked out
192 pub fn new() -> WorktreePruneOptions
{
194 let mut raw
= mem
::zeroed();
196 raw
::git_worktree_prune_options_init(
198 raw
::GIT_WORKTREE_PRUNE_OPTIONS_VERSION
202 WorktreePruneOptions { raw }
206 /// Controls whether valid (still existing on the filesystem) worktrees
209 /// Defaults to false
210 pub fn valid(&mut self, valid
: bool
) -> &mut WorktreePruneOptions
{
211 self.flag(raw
::GIT_WORKTREE_PRUNE_VALID
, valid
)
214 /// Controls whether locked worktrees will be pruned
216 /// Defaults to false
217 pub fn locked(&mut self, locked
: bool
) -> &mut WorktreePruneOptions
{
218 self.flag(raw
::GIT_WORKTREE_PRUNE_LOCKED
, locked
)
221 /// Controls whether the actual working tree on the filesystem is recursively removed
223 /// Defaults to false
224 pub fn working_tree(&mut self, working_tree
: bool
) -> &mut WorktreePruneOptions
{
225 self.flag(raw
::GIT_WORKTREE_PRUNE_WORKING_TREE
, working_tree
)
228 fn flag(&mut self, flag
: raw
::git_worktree_prune_t
, on
: bool
) -> &mut WorktreePruneOptions
{
230 self.raw
.flags
|= flag
as u32;
232 self.raw
.flags
&= !(flag
as u32);
237 /// Get a set of raw prune options to be used with `git_worktree_prune`
238 pub fn raw(&mut self) -> *mut raw
::git_worktree_prune_options
{
243 impl Binding
for Worktree
{
244 type Raw
= *mut raw
::git_worktree
;
245 unsafe fn from_raw(ptr
: *mut raw
::git_worktree
) -> Worktree
{
246 Worktree { raw: ptr }
248 fn raw(&self) -> *mut raw
::git_worktree
{
253 impl Drop
for Worktree
{
255 unsafe { raw::git_worktree_free(self.raw) }
261 use crate::WorktreeAddOptions
;
262 use crate::WorktreeLockStatus
;
264 use tempfile
::TempDir
;
267 fn smoke_add_no_ref() {
268 let (_td
, repo
) = crate::test
::repo_init();
270 let wtdir
= TempDir
::new().unwrap();
271 let wt_path
= wtdir
.path().join("tree-no-ref-dir");
272 let opts
= WorktreeAddOptions
::new();
274 let wt
= repo
.worktree("tree-no-ref", &wt_path
, Some(&opts
)).unwrap();
275 assert_eq
!(wt
.name(), Some("tree-no-ref"));
277 wt
.path().canonicalize().unwrap(),
278 wt_path
.canonicalize().unwrap()
280 let status
= wt
.is_locked().unwrap();
281 assert_eq
!(status
, WorktreeLockStatus
::Unlocked
);
285 fn smoke_add_locked() {
286 let (_td
, repo
) = crate::test
::repo_init();
288 let wtdir
= TempDir
::new().unwrap();
289 let wt_path
= wtdir
.path().join("locked-tree");
290 let mut opts
= WorktreeAddOptions
::new();
293 let wt
= repo
.worktree("locked-tree", &wt_path
, Some(&opts
)).unwrap();
294 // shouldn't be able to lock a worktree that was created locked
295 assert
!(wt
.lock(Some("my reason")).is_err());
296 assert_eq
!(wt
.name(), Some("locked-tree"));
298 wt
.path().canonicalize().unwrap(),
299 wt_path
.canonicalize().unwrap()
301 assert_eq
!(wt
.is_locked().unwrap(), WorktreeLockStatus
::Locked(None
));
302 assert
!(wt
.unlock().is_ok());
303 assert
!(wt
.lock(Some("my reason")).is_ok());
305 wt
.is_locked().unwrap(),
306 WorktreeLockStatus
::Locked(Some("my reason".to_string()))
311 fn smoke_add_from_branch() {
312 let (_td
, repo
) = crate::test
::repo_init();
314 let (wt_top
, branch
) = crate::test
::worktrees_env_init(&repo
);
315 let wt_path
= wt_top
.path().join("test");
316 let mut opts
= WorktreeAddOptions
::new();
317 let reference
= branch
.into_reference();
318 opts
.reference(Some(&reference
));
321 .worktree("test-worktree", &wt_path
, Some(&opts
))
323 assert_eq
!(wt
.name(), Some("test-worktree"));
325 wt
.path().canonicalize().unwrap(),
326 wt_path
.canonicalize().unwrap()
328 let status
= wt
.is_locked().unwrap();
329 assert_eq
!(status
, WorktreeLockStatus
::Unlocked
);