Allow checkouts of repositories into non-empty directories#2508
Allow checkouts of repositories into non-empty directories#2508Jan Walther (j-walther) wants to merge 5 commits intoGitoxideLabs:mainfrom
Conversation
3c5b2d5 to
352d216
Compare
Sebastian Thiel (Byron)
left a comment
There was a problem hiding this comment.
Thanks, but I think this needs test coverage.
I would also be afraid that it now allows writing non-empty directories, wiping data in the process.
What would you expect tests for this to look like? Checking out into a non empty directory with the flag both set and not set? The latter would just be a regular checkout. |
|
There needs to be a test that fails if the change of this PR isn't present.
It might already exist as well, in which case you can point it out to me. |
dfadc8b to
592b69d
Compare
|
I've added the tests. What I think should be done is to change the |
Sebastian Thiel (Byron)
left a comment
There was a problem hiding this comment.
I don't think I understand the premise. The reason that it won't allow cloning into a non-empty directory is that it will brutally overwrite everything. Git also rejects this, AFAIK.
❯ git clone https://github.com/GitoxideLabs/gitoxide ./src/
fatal: destination path './src' already exists and is not an empty directory.
The PR title is "Allow checkouts of empty repositories". Thus a test should show that empty repositories couldn't be checked out before, and after the fix they can.
I've added the tests. What I think should be done is to change the Default implementation to set destination_must_be_empty to true by default.
I agree, and wonder why I didn't that before.
One problem remains: I don't understand what the PR is trying to fix.
There was a problem hiding this comment.
Pull request overview
This PR adjusts gix::clone::PrepareFetch::new() to honor the caller-provided gix::create::Options instead of unconditionally requiring an empty destination directory for worktree clones, aligning clone initialization behavior with the documented semantics of create::Options. It also adds regression tests covering the new default behavior and the opt-in strict mode.
Changes:
- Stop forcing
create_opts.destination_must_be_empty = trueinPrepareFetch::new_inner(). - Add a test proving fetch+checkout into a non-empty destination directory works by default (and preserves pre-existing files).
- Add a test proving
destination_must_be_empty: truestill rejects non-empty destinations for worktree clones.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| gix/src/clone/mod.rs | Removes the unconditional override of destination_must_be_empty, so clone respects caller-provided creation options. |
| gix/tests/gix/clone.rs | Adds coverage for cloning into non-empty worktree destinations by default and for the strict “must be empty” option. |
| #[allow(clippy::result_large_err)] | ||
| fn new_inner( | ||
| mut url: gix_url::Url, | ||
| path: &std::path::Path, | ||
| kind: crate::create::Kind, | ||
| mut create_opts: crate::create::Options, | ||
| create_opts: crate::create::Options, | ||
| open_opts: crate::open::Options, | ||
| ) -> Result<Self, Error> { | ||
| create_opts.destination_must_be_empty = true; | ||
| let mut repo = crate::ThreadSafeRepository::init_opts(path, kind, create_opts, open_opts)?.to_thread_local(); | ||
| url.canonicalize(repo.options.current_dir_or_empty()) |
There was a problem hiding this comment.
The PR title mentions "checkouts of empty repositories", but the code change (and new tests) are about allowing clone/fetch+checkout into a non-empty destination directory by default (unless destination_must_be_empty is set). Consider updating the PR title (or description) to match the actual behavior change to avoid confusion in release notes/changelog context.
There was a problem hiding this comment.
Done
Before my changes every code path set the |
|
I've updated the tests to better reflect the change and made it so |
d090222 to
e6727f1
Compare
| destination_must_be_empty: false, | ||
| ..Default::default() | ||
| }, | ||
| ) |
There was a problem hiding this comment.
Restores previous behaviour which was also enforced by the init_into_non_empty_directory_is_allowed_by_default test.
| create_opts: crate::create::Options, | ||
| open_opts: crate::open::Options, | ||
| ) -> Result<Self, Error> { |
There was a problem hiding this comment.
Now that PrepareFetch::new_inner() no longer forces destination_must_be_empty = true, callers can legitimately pass destination_must_be_empty: false. However, PrepareFetch/PrepareCheckout currently auto-clean up in Drop by calling remove_dir_all(repo.workdir()…), which will delete the entire destination directory—including pre-existing user files—if clone/fetch/checkout fails or the handle is dropped without persist(). Please adjust cleanup to be safe for non-empty destinations (e.g., track whether the destination was newly created/empty and only remove_dir_all in that case, otherwise only remove the created .git dir or disable auto-cleanup and require explicit cleanup by the caller).
| create_opts: crate::create::Options, | |
| open_opts: crate::open::Options, | |
| ) -> Result<Self, Error> { | |
| mut create_opts: crate::create::Options, | |
| open_opts: crate::open::Options, | |
| ) -> Result<Self, Error> { | |
| create_opts.destination_must_be_empty = true; |
| @@ -119,6 +119,15 @@ pub struct Options { | |||
| pub fs_capabilities: Option<gix_fs::Capabilities>, | |||
| } | |||
|
|
|||
| impl Default for Options { | |||
| fn default() -> Self { | |||
| Options { | |||
| destination_must_be_empty: true, | |||
| fs_capabilities: None, | |||
| } | |||
There was a problem hiding this comment.
create::Options's field docs say worktree repos can be initialized into a non-empty directory “by default” (as long as no .git exists), but Default for Options now sets destination_must_be_empty: true, which makes the default behavior the opposite. Please align documentation and behavior (either change the default back to false and have clone code set it to true, or update the docs to state the default is strict and that gix::init()/CLI init explicitly override it to false).
This line effectively prevented checking out in non-empty directories in all cases.