-
Notifications
You must be signed in to change notification settings - Fork 413
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only noticed this after me spending two days writing my worktree bindings...
@foriequal0 could you add a rough todo list of what needs to happen for this to be done? I might have some time to put in, and as a heavy worktree user I'd love this to make its way into git2-rs based tools. :-) |
The systest changes were necessary due to the actual field name used in the C library being “ref” and not being able to name the field the same in the Rust struct because “ref” is a reserved keyword.
The type it aliases, git_indexer_progress, is still checked but this prevents spurious warnings about using deprecated fields.
I rebased it to resolve merge conflict. @kamalmarhubi I forgot this while waiting for a review. I think the feature is done. After finishing the review, I have to squash some commits before merging. |
e.g. branch uses branches, find_branch, branch to list, get, create branches.
ping @joshtriplett or @alexcrichton as you've both recently merged prs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, thanks for picking this up!
src/repo.rs
Outdated
@@ -162,6 +163,19 @@ impl Repository { | |||
} | |||
} | |||
|
|||
/// Attempt to open an already-existing repository from a worktree. | |||
pub fn open_from_worktree(worktree: &Worktree) -> Result<Repository, Error> { | |||
crate::init(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be skipped since the presence of Worktree
implies it's already been called.
systest/build.rs
Outdated
@@ -41,5 +43,6 @@ fn main() { | |||
cfg.skip_roundtrip(|t| t == "git_clone_options" || t == "git_submodule_update_options"); | |||
|
|||
cfg.skip_type(|t| t == "__enum_ty"); | |||
cfg.skip_type(|t| t == "git_transfer_progress"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come this is skipped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't write this commit. I think @mkeeler wanted to turn off deprecation warning according to the commit message, but it doesn't complaint on my computer, and I think it is not reponsibility of this PR. I'll remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this may still want to get removed?
src/test.rs
Outdated
@@ -17,6 +18,118 @@ macro_rules! t { | |||
}; | |||
} | |||
|
|||
// `repo_test! will | |||
macro_rules! repo_test { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may be missing something, but how come a macro for this was added? Could the tests be written just against the right function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I asume that @mjkeeler wanted to make sure that same code should have same functionalities even if underlying repositories are different (normal repo, normal repo's worktree, bare repo, bare repo's worktree). I've finished his implementation, but I still think it is the libgit2's responsibility since git2-rs is just a binding. Should I remove this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's good to keep some tests in this repository, but whether or not it's done with a macro is up to you. I personally prefer to avoid macros where possible.
src/test.rs
Outdated
(tds, worktree_repo) | ||
} | ||
|
||
pub fn repo_init_typical_worktree() -> (TempDirs, Repository) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these functions seem related to worktree-testing, perhaps they could be scoped to the worktree tests submodule?
src/worktree.rs
Outdated
/// filesystem and that the metadata is correct | ||
pub fn validate(&self) -> Result<(), Error> { | ||
unsafe { | ||
call::c_try(raw::git_worktree_validate(call::convert(&self.raw)))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this use the try_call!
macro instead of the call
module directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same below)
src/worktree.rs
Outdated
let v = buf.to_vec(); | ||
Ok(WorktreeLockStatus::Locked(match v.len() { | ||
0 => None, | ||
_ => String::from_utf8(v).ok(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this use .unwrap()
since it's expected to always be utf-8?
src/worktree.rs
Outdated
|
||
/// Opens the repository from the worktree | ||
pub fn to_repository(&self) -> Result<Repository, Error> { | ||
Repository::open_from_worktree(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer to stick close to the original git2
API where possible, so could this and the other convenience alias be removed?
src/worktree.rs
Outdated
); | ||
|
||
opts.lock = self.lock as c_int; | ||
opts.reference = crate::call::convert(&self.reference.as_ref().map(|o| o.raw())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of the convert
function could this do the conversion manually?
Also I think it might be good to store the git_worktree_add_options
directly in this structure.
src/worktree.rs
Outdated
/// This method is unsafe as the returned value may have pointers to the | ||
/// interior of this structure | ||
pub unsafe fn raw(&self) -> raw::git_worktree_prune_options { | ||
let mut opts = mem::zeroed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this opts
be stored directly in the structure?
src/branch.rs
Outdated
@@ -153,8 +153,7 @@ impl<'repo> Drop for Branches<'repo> { | |||
mod tests { | |||
use crate::BranchType; | |||
|
|||
#[test] | |||
fn smoke() { | |||
repo_test!(smoke, (Typical, TypicalWorktree, BareWorktree), |_| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably don't need to update old tests like this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll revert old tests, but keep worktree tests. I'll move and worktree tests. Let smoke tests be simple smoke tests.repo_test
macro to worktree.rs
src/test.rs
Outdated
@@ -17,6 +18,118 @@ macro_rules! t { | |||
}; | |||
} | |||
|
|||
// `repo_test! will | |||
macro_rules! repo_test { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's good to keep some tests in this repository, but whether or not it's done with a macro is up to you. I personally prefer to avoid macros where possible.
Looks like review comments were all addressed, is that right @foriequal0? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few more small comments!
src/worktree.rs
Outdated
|
||
// It is the current belief that a `Worktree` can be sent among threads, or | ||
// even shared among threads in a mutex | ||
unsafe impl Send for Worktree {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry I think I missed this earlier, but are you sure of this? Is this documented in libgit2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the best I could find:
Unless otherwise specified, libgit2 objects cannot be safely accessed by multiple threads simultaneously.
[...]
Use an object from a single thread at a time. Most data structures do not guard against concurrent access themselves. This is because they are rarely used in isolation and it makes more sense to synchronize access via a larger lock or similar mechanism.
—https://github.com/libgit2/libgit2/blob/master/docs/threading.md
I think this can be read as they are Send but not Sync?
Looks like Repository has been Send for a while: 51f1376
And the comment this was modeled after was added around the same time: 2b0f5cb#diff-8a44ff48a251d03d58e6d3c73e8664553e2aa7b2a925641dd6c3d2f81c0d2e11
(Both changes by you, almost six years ago!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original version of that file was clearer on this point:
You may safely use any libgit2 object from any thread, though there may be issues depending on the cryptographic libraries libgit2 or its dependencies link to (more on this later).
—libgit2/libgit2@15bea02#diff-bb59cfb2bcd0e444b33e25dc3f1e127e095d72379150cc82d408afa63b9903bf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah as @joshtriplett pointed out below libgit2's policy isn't enough to dictate whether things are Send
or Sync
in Rust. You'll need to devle into the internals and audit what's happening to see if it's safe. I think I did that for git_repository
a long time ago, but if this wants to stay then it should happen for git_worktree
as well. It's always ok to drop this unsafe impl
for now though and add it back later if it works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I didn't notice that. I'll remove it.
systest/build.rs
Outdated
@@ -41,5 +43,6 @@ fn main() { | |||
cfg.skip_roundtrip(|t| t == "git_clone_options" || t == "git_submodule_update_options"); | |||
|
|||
cfg.skip_type(|t| t == "__enum_ty"); | |||
cfg.skip_type(|t| t == "git_transfer_progress"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this may still want to get removed?
On Mon, Nov 16, 2020 at 10:30:06AM -0800, Kamal Marhubi wrote:
@kamalmarhubi commented on this pull request.
> +use std::path::Path;
+use std::ptr;
+use std::str;
+use std::{marker, mem};
+
+/// An owned git worktree
+///
+/// This structure corresponds to a `git_worktree` in libgit2.
+//
+pub struct Worktree {
+ raw: *mut raw::git_worktree,
+}
+
+// It is the current belief that a `Worktree` can be sent among threads, or
+// even shared among threads in a mutex
+unsafe impl Send for Worktree {}
This is the best I could find:
> Unless otherwise specified, libgit2 objects cannot be safely accessed by multiple threads simultaneously.
> [...]
> Use an object from a single thread at a time. Most data structures do not guard against concurrent access themselves. This is because they are rarely used in isolation and it makes more sense to synchronize access via a larger lock or similar mechanism.
—https://github.com/libgit2/libgit2/blob/master/docs/threading.md
I think this can be read as they are Send but not Sync?
Not necessarily. `Send` would mean you can share a read-only reference
to them across threads. That would in turn mean you can call functions
that only need a read-only reference. But in some cases, libgit2's
structures contain internal mutability (on the C side), which means even
"read-only" methods are not safe to call from multiple threads.
Marking any libgit2 structure `Send` means carefully checking for any
such internal mutability, and having high confidence that no new
internal mutability will be introduced in the future.
|
Thanks for the review! I think you can squash merge if it is okay or I should rebase commits before merging them. |
OK everything looks great to me, thanks again! And no worries, I use the squash-and-merge button so that happens automatically. |
@foriequal0 To the best of your knowledge, does this fully supersede #404 , or is there additional work in that PR that needs updating and merging? |
@joshtriplett I think there is no additional work for this :) |
#404 has been draft for a while. Since @mkeeler said 'Feel free to take it over'(comment), I've appended some commits after it.
I've intentionally not cleaned commits to preserve history and intensions. I'll clean up the history as it get reviewed.