Skip to content

More Better Worktree Support #603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dfd5fc9
Intial support for working with worktrees
mkeeler Apr 26, 2020
8fca1ee
Ignore the deprecated git_transfer_progress type alias in systest
mkeeler Apr 27, 2020
714beb6
First round of experimental multi-repo testing
mkeeler May 14, 2020
9e89b47
Fix macro hygene error in `repo_test`
foriequal0 Oct 27, 2020
a4be5a9
Match worktree version const types with other version consts
foriequal0 Aug 5, 2020
fbd13a1
Match worktree naming conventions with other entities
foriequal0 Aug 5, 2020
c55c334
Move conversions to under its namespaces and match naming convention
foriequal0 Aug 5, 2020
8a79cdd
Match WorktreeAddOptions style with other options
foriequal0 Aug 5, 2020
daba2aa
Use ptr::null_mut() rather than 0
foriequal0 Aug 5, 2020
b8ab3a3
Take `Option<>` where API may take NULL as opts
foriequal0 Aug 5, 2020
8db7070
Create TempDirs to hold multiple `TempDir`s
foriequal0 Aug 5, 2020
6d31b71
Implement multi-repo testing
foriequal0 Aug 5, 2020
8e17a98
Use repo.workdir() to get working directory
foriequal0 Aug 5, 2020
b51bf5c
Remove unnecessary and dangerous conversion for Cstring into *mut c_char
foriequal0 Aug 25, 2020
7c820bc
Fix comments
foriequal0 Aug 25, 2020
42cd516
Remove println
foriequal0 Aug 25, 2020
a81f3ad
Make Worktree::is_prunable return Result
foriequal0 Aug 25, 2020
fab731c
fixup! Remove init
foriequal0 Oct 31, 2020
966c34d
fixup! use try_call!
foriequal0 Oct 31, 2020
491efd4
fixup! use unwrap since it's expected to always be utf-8
foriequal0 Oct 31, 2020
7ba981c
fixup! Remove conversion aliases
foriequal0 Oct 31, 2020
3327e21
fixup! Store raw git_worktree_add_options in WorktreeAddOptions
foriequal0 Oct 31, 2020
79d4459
fixup! Store raw git_worktree_prune_options in WorktreePruneOptions
foriequal0 Oct 31, 2020
dd529ae
fixup! Remove repo_test! macros
foriequal0 Nov 5, 2020
f73d6a2
fixup! Make sure `Reference` outlive `WorktreeAddOptions`
foriequal0 Nov 5, 2020
858a446
fixup! Remove unnecessary `unsafe impl Send`
foriequal0 Nov 17, 2020
d0418f7
fixup! Don't skip git_transfer_progress
foriequal0 Nov 17, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ structopt = "0.3"
time = "0.1.39"
tempfile = "3.1.0"
thread-id = "3.3.0" # remove when we work with minimal-versions without it
paste = "0.1.12"

[features]
unstable = []
Expand Down
70 changes: 70 additions & 0 deletions libgit2-sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,34 @@ git_enum! {
}
}

#[repr(C)]
pub struct git_worktree_add_options {
pub version: c_uint,
pub lock: c_int,
pub reference: *mut git_reference,
}

pub const GIT_WORKTREE_ADD_OPTIONS_VERSION: c_uint = 1;

git_enum! {
pub enum git_worktree_prune_t {
/* Prune working tree even if working tree is valid */
GIT_WORKTREE_PRUNE_VALID = 1 << 0,
/* Prune working tree even if it is locked */
GIT_WORKTREE_PRUNE_LOCKED = 1 << 1,
/* Prune checked out working tree */
GIT_WORKTREE_PRUNE_WORKING_TREE = 1 << 2,
}
}

#[repr(C)]
pub struct git_worktree_prune_options {
pub version: c_uint,
pub flags: u32,
}

pub const GIT_WORKTREE_PRUNE_OPTIONS_VERSION: c_uint = 1;

extern "C" {
// threads
pub fn git_libgit2_init() -> c_int;
Expand Down Expand Up @@ -3780,6 +3808,48 @@ extern "C" {
) -> c_int;

pub fn git_libgit2_opts(option: c_int, ...) -> c_int;

// Worktrees
pub fn git_worktree_list(out: *mut git_strarray, repo: *mut git_repository) -> c_int;
pub fn git_worktree_lookup(
out: *mut *mut git_worktree,
repo: *mut git_repository,
name: *const c_char,
) -> c_int;
pub fn git_worktree_open_from_repository(
out: *mut *mut git_worktree,
repo: *mut git_repository,
) -> c_int;
pub fn git_worktree_free(wt: *mut git_worktree);
pub fn git_worktree_validate(wt: *const git_worktree) -> c_int;
pub fn git_worktree_add_options_init(
opts: *mut git_worktree_add_options,
version: c_uint,
) -> c_int;
pub fn git_worktree_add(
out: *mut *mut git_worktree,
repo: *mut git_repository,
name: *const c_char,
path: *const c_char,
opts: *const git_worktree_add_options,
) -> c_int;
pub fn git_worktree_lock(wt: *mut git_worktree, reason: *const c_char) -> c_int;
pub fn git_worktree_unlock(wt: *mut git_worktree) -> c_int;
pub fn git_worktree_is_locked(reason: *mut git_buf, wt: *const git_worktree) -> c_int;
pub fn git_worktree_name(wt: *const git_worktree) -> *const c_char;
pub fn git_worktree_path(wt: *const git_worktree) -> *const c_char;
pub fn git_worktree_prune_options_init(
opts: *mut git_worktree_prune_options,
version: c_uint,
) -> c_int;
pub fn git_worktree_is_prunable(
wt: *mut git_worktree,
opts: *mut git_worktree_prune_options,
) -> c_int;
pub fn git_worktree_prune(
wt: *mut git_worktree,
opts: *mut git_worktree_prune_options,
) -> c_int;
}

pub fn init() {
Expand Down
2 changes: 1 addition & 1 deletion src/blame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ mod tests {
let (_td, repo) = crate::test::repo_init();
let mut index = repo.index().unwrap();

let root = repo.path().parent().unwrap();
let root = repo.workdir().unwrap();
fs::create_dir(&root.join("foo")).unwrap();
File::create(&root.join("foo/bar")).unwrap();
index.add_path(Path::new("foo/bar")).unwrap();
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub use crate::time::{IndexTime, Time};
pub use crate::tree::{Tree, TreeEntry, TreeIter, TreeWalkMode, TreeWalkResult};
pub use crate::treebuilder::TreeBuilder;
pub use crate::util::IntoCString;
pub use crate::worktree::{Worktree, WorktreeAddOptions, WorktreeLockStatus, WorktreePruneOptions};

// Create a convinience method on bitflag struct which checks the given flag
macro_rules! is_bit_set {
Expand Down Expand Up @@ -686,6 +687,7 @@ mod tagforeach;
mod time;
mod tree;
mod treebuilder;
mod worktree;

fn init() {
static INIT: Once = Once::new();
Expand Down
60 changes: 60 additions & 0 deletions src/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::stash::{stash_cb, StashApplyOptions, StashCbData};
use crate::string_array::StringArray;
use crate::tagforeach::{tag_foreach_cb, TagForeachCB, TagForeachData};
use crate::util::{self, path_to_repo_path, Binding};
use crate::worktree::{Worktree, WorktreeAddOptions};
use crate::CherrypickOptions;
use crate::RevertOptions;
use crate::{
Expand Down Expand Up @@ -162,6 +163,18 @@ impl Repository {
}
}

/// Attempt to open an already-existing repository from a worktree.
pub fn open_from_worktree(worktree: &Worktree) -> Result<Repository, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_open_from_worktree(
&mut ret,
worktree.raw()
));
Ok(Binding::from_raw(ret))
}
}

/// Attempt to open an already-existing repository at or above `path`
///
/// This starts at `path` and looks up the filesystem hierarchy
Expand Down Expand Up @@ -2791,6 +2804,53 @@ impl Repository {
Ok(Binding::from_raw(ret))
}
}

/// Lists all the worktrees for the repository
pub fn worktrees(&self) -> Result<StringArray, Error> {
let mut arr = raw::git_strarray {
strings: ptr::null_mut(),
count: 0,
};
unsafe {
try_call!(raw::git_worktree_list(&mut arr, self.raw));
Ok(Binding::from_raw(arr))
}
}

/// Opens a worktree by name for the given repository
///
/// This can open any worktree that the worktrees method returns.
pub fn find_worktree(&self, name: &str) -> Result<Worktree, Error> {
let mut raw = ptr::null_mut();
let raw_name = CString::new(name)?;
unsafe {
try_call!(raw::git_worktree_lookup(&mut raw, self.raw, raw_name));
Ok(Binding::from_raw(raw))
}
}

/// Creates a new worktree for the repository
pub fn worktree<'a>(
&'a self,
name: &str,
path: &Path,
opts: Option<&WorktreeAddOptions<'a>>,
) -> Result<Worktree, Error> {
let mut raw = ptr::null_mut();
let raw_name = CString::new(name)?;
let raw_path = path.into_c_string()?;

unsafe {
try_call!(raw::git_worktree_add(
&mut raw,
self.raw,
raw_name,
raw_path,
opts.map(|o| o.raw())
));
Ok(Binding::from_raw(raw))
}
}
}

impl Binding for Repository {
Expand Down
10 changes: 9 additions & 1 deletion src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::ptr;
use tempfile::TempDir;
use url::Url;

use crate::{Oid, Repository, RepositoryInitOptions};
use crate::{Branch, Oid, Repository, RepositoryInitOptions};

macro_rules! t {
($e:expr) => {
Expand Down Expand Up @@ -56,6 +56,14 @@ pub fn path2url(path: &Path) -> String {
Url::from_file_path(path).unwrap().to_string()
}

pub fn worktrees_env_init(repo: &Repository) -> (TempDir, Branch<'_>) {
let oid = repo.head().unwrap().target().unwrap();
let commit = repo.find_commit(oid).unwrap();
let branch = repo.branch("wt-branch", &commit, true).unwrap();
let wtdir = TempDir::new().unwrap();
(wtdir, branch)
}

#[cfg(windows)]
pub fn realpath(original: &Path) -> io::Result<PathBuf> {
Ok(original.to_path_buf())
Expand Down
Loading