Skip to content

Commit 9699478

Browse files
committed
Intial support for working with worktrees
1 parent 2a6d566 commit 9699478

File tree

6 files changed

+473
-1
lines changed

6 files changed

+473
-1
lines changed

libgit2-sys/lib.rs

+70
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,34 @@ git_enum! {
17581758
}
17591759
}
17601760

1761+
#[repr(C)]
1762+
pub struct git_worktree_add_options {
1763+
pub version: c_uint,
1764+
pub lock: c_int,
1765+
pub reference: *mut git_reference,
1766+
}
1767+
1768+
pub const GIT_WORKTREE_ADD_OPTIONS_VERSION: u32 = 1;
1769+
1770+
git_enum! {
1771+
pub enum git_worktree_prune_t {
1772+
/* Prune working tree even if working tree is valid */
1773+
GIT_WORKTREE_PRUNE_VALID = 1 << 0,
1774+
/* Prune working tree even if it is locked */
1775+
GIT_WORKTREE_PRUNE_LOCKED = 1 << 1,
1776+
/* Prune checked out working tree */
1777+
GIT_WORKTREE_PRUNE_WORKING_TREE = 1 << 2,
1778+
}
1779+
}
1780+
1781+
#[repr(C)]
1782+
pub struct git_worktree_prune_options {
1783+
pub version: c_uint,
1784+
pub flags: u32,
1785+
}
1786+
1787+
pub const GIT_WORKTREE_PRUNE_OPTIONS_VERSION: u32 = 1;
1788+
17611789
extern "C" {
17621790
// threads
17631791
pub fn git_libgit2_init() -> c_int;
@@ -3645,6 +3673,48 @@ extern "C" {
36453673
location: git_apply_location_t,
36463674
options: *const git_apply_options,
36473675
) -> c_int;
3676+
3677+
// Worktrees
3678+
pub fn git_worktree_list(out: *mut git_strarray, repo: *mut git_repository) -> c_int;
3679+
pub fn git_worktree_lookup(
3680+
out: *mut *mut git_worktree,
3681+
repo: *mut git_repository,
3682+
name: *const c_char,
3683+
) -> c_int;
3684+
pub fn git_worktree_open_from_repository(
3685+
out: *mut *mut git_worktree,
3686+
repo: *mut git_repository,
3687+
) -> c_int;
3688+
pub fn git_worktree_free(wt: *mut git_worktree);
3689+
pub fn git_worktree_validate(wt: *const git_worktree) -> c_int;
3690+
pub fn git_worktree_add_init_options(
3691+
opts: *mut git_worktree_add_options,
3692+
version: c_uint,
3693+
) -> c_int;
3694+
pub fn git_worktree_add(
3695+
out: *mut *mut git_worktree,
3696+
repo: *mut git_repository,
3697+
name: *const c_char,
3698+
path: *const c_char,
3699+
opts: *const git_worktree_add_options,
3700+
) -> c_int;
3701+
pub fn git_worktree_lock(wt: *mut git_worktree, reason: *const c_char) -> c_int;
3702+
pub fn git_worktree_unlock(wt: *mut git_worktree) -> c_int;
3703+
pub fn git_worktree_is_locked(reason: *mut git_buf, wt: *const git_worktree) -> c_int;
3704+
pub fn git_worktree_name(wt: *const git_worktree) -> *const c_char;
3705+
pub fn git_worktree_path(wt: *const git_worktree) -> *const c_char;
3706+
pub fn git_worktree_prune_init_options(
3707+
opts: *mut git_worktree_prune_options,
3708+
version: c_uint,
3709+
) -> c_int;
3710+
pub fn git_worktree_is_prunable(
3711+
wt: *mut git_worktree,
3712+
opts: *mut git_worktree_prune_options,
3713+
) -> c_int;
3714+
pub fn git_worktree_prune(
3715+
wt: *mut git_worktree,
3716+
opts: *mut git_worktree_prune_options,
3717+
) -> c_int;
36483718
}
36493719

36503720
pub fn init() {

src/call.rs

+6
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ mod impls {
9393
}
9494
}
9595

96+
impl Convert<*mut libc::c_char> for CString {
97+
fn convert(&self) -> *mut libc::c_char {
98+
self.as_ptr() as *mut libc::c_char
99+
}
100+
}
101+
96102
impl<T, U: Convert<*const T>> Convert<*const T> for Option<U> {
97103
fn convert(&self) -> *const T {
98104
self.as_ref().map(|s| s.convert()).unwrap_or(ptr::null())

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pub use crate::time::{IndexTime, Time};
129129
pub use crate::tree::{Tree, TreeEntry, TreeIter, TreeWalkMode, TreeWalkResult};
130130
pub use crate::treebuilder::TreeBuilder;
131131
pub use crate::util::IntoCString;
132+
pub use crate::worktree::{Worktree, WorktreeAddOptions, WorktreeLockStatus, WorktreePruneOptions};
132133

133134
// Create a convinience method on bitflag struct which checks the given flag
134135
macro_rules! is_bit_set {
@@ -666,6 +667,7 @@ mod tag;
666667
mod time;
667668
mod tree;
668669
mod treebuilder;
670+
mod worktree;
669671

670672
fn init() {
671673
static INIT: Once = Once::new();

src/repo.rs

+61
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::oid_array::OidArray;
1515
use crate::stash::{stash_cb, StashApplyOptions, StashCbData};
1616
use crate::string_array::StringArray;
1717
use crate::util::{self, path_to_repo_path, Binding};
18+
use crate::worktree::{Worktree, WorktreeAddOptions};
1819
use crate::CherrypickOptions;
1920
use crate::{
2021
init, raw, AttrCheckFlags, Buf, Error, Object, Remote, RepositoryOpenFlags, RepositoryState,
@@ -2700,6 +2701,66 @@ impl Repository {
27002701
Ok(())
27012702
}
27022703
}
2704+
2705+
/// Lists all the worktrees for the repository
2706+
pub fn worktrees(&self) -> Result<StringArray, Error> {
2707+
let mut arr = raw::git_strarray {
2708+
strings: 0 as *mut *mut c_char,
2709+
count: 0,
2710+
};
2711+
unsafe {
2712+
try_call!(raw::git_worktree_list(&mut arr, self.raw));
2713+
Ok(Binding::from_raw(arr))
2714+
}
2715+
}
2716+
2717+
/// Opens a worktree by name for the given repository
2718+
///
2719+
/// This can open any worktree that the worktrees method returns.
2720+
pub fn worktree_lookup(&self, name: &str) -> Result<Worktree, Error> {
2721+
let mut raw = ptr::null_mut();
2722+
let raw_name = CString::new(name)?;
2723+
unsafe {
2724+
try_call!(raw::git_worktree_lookup(&mut raw, self.raw, raw_name));
2725+
Ok(Binding::from_raw(raw))
2726+
}
2727+
}
2728+
2729+
/// Open a worktree of a the repository
2730+
///
2731+
/// If a repository is not the main tree but a worktree, this
2732+
/// function will look up the worktree inside the parent
2733+
/// repository and create a new `git_worktree` structure.
2734+
pub fn worktree_open_from_repository(&self) -> Result<Worktree, Error> {
2735+
let mut raw = ptr::null_mut();
2736+
unsafe {
2737+
try_call!(raw::git_worktree_open_from_repository(&mut raw, self.raw));
2738+
Ok(Binding::from_raw(raw))
2739+
}
2740+
}
2741+
2742+
/// Creates a new worktree for the repository
2743+
pub fn worktree_add(
2744+
&self,
2745+
name: &str,
2746+
path: &Path,
2747+
opts: &WorktreeAddOptions<'_>,
2748+
) -> Result<Worktree, Error> {
2749+
let mut raw = ptr::null_mut();
2750+
let raw_name = CString::new(name)?;
2751+
let raw_path = path.into_c_string()?;
2752+
2753+
unsafe {
2754+
try_call!(raw::git_worktree_add(
2755+
&mut raw,
2756+
self.raw,
2757+
raw_name,
2758+
raw_path,
2759+
&opts.raw()
2760+
));
2761+
Ok(Binding::from_raw(raw))
2762+
}
2763+
}
27032764
}
27042765

27052766
impl Binding for Repository {

src/test.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::ptr;
66
use tempfile::TempDir;
77
use url::Url;
88

9-
use crate::{Oid, Repository};
9+
use crate::{Branch, Oid, Repository};
1010

1111
macro_rules! t {
1212
($e:expr) => {
@@ -54,6 +54,14 @@ pub fn path2url(path: &Path) -> String {
5454
Url::from_file_path(path).unwrap().to_string()
5555
}
5656

57+
pub fn worktrees_env_init(repo: &Repository) -> (TempDir, Branch<'_>) {
58+
let oid = repo.head().unwrap().target().unwrap();
59+
let commit = repo.find_commit(oid).unwrap();
60+
let branch = repo.branch("wt-branch", &commit, true).unwrap();
61+
let wtdir = TempDir::new().unwrap();
62+
(wtdir, branch)
63+
}
64+
5765
#[cfg(windows)]
5866
pub fn realpath(original: &Path) -> io::Result<PathBuf> {
5967
Ok(original.to_path_buf())

0 commit comments

Comments
 (0)