Skip to content

Allow checkouts of repositories into non-empty directories#2508

Merged
Sebastian Thiel (Byron) merged 2 commits into
GitoxideLabs:mainfrom
j-walther:feat/allow-empty-dir
May 12, 2026
Merged

Allow checkouts of repositories into non-empty directories#2508
Sebastian Thiel (Byron) merged 2 commits into
GitoxideLabs:mainfrom
j-walther:feat/allow-empty-dir

Conversation

@j-walther

@j-walther Jan Walther (j-walther) commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Allow gix::clone::PrepareFetch callers to clone and check out into a destination directory that is not empty.

Before this change, PrepareFetch::new() unconditionally overrode create::Options::destination_must_be_empty to true regardless of what the caller passed, so the flag was effectively non-functional and there was no way to opt in to a non-empty destination through the public API.

gix::create::Options::destination_must_be_empty now controls this for non-bare repositories:

  • None (default for gix::init) — the directory may already contain files as long as no .git directory is present.
  • Some(true) (effective default for prepare_clone / prepare_clone_bare) — require an empty destination, preserving the prior clone behavior.
  • Some(false) — explicitly allow a non-empty destination for clones.

Bare repositories always require an empty destination.

If a clone fails before completion and the prepare handle is dropped, only the .git directory created by the clone is removed; any pre-existing user files in the destination are preserved.

@Byron Sebastian Thiel (Byron) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@j-walther

Copy link
Copy Markdown
Contributor Author

I would also be afraid that it now allows writing non-empty directories, wiping data in the process.
The current state is that it's effectively impossible to check-out into a non-empty directory making that user-facing flag effectively superfluous.

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.

@Byron

Copy link
Copy Markdown
Member

There needs to be a test that fails if the change of this PR isn't present.
Also, there must be a test that shows non-empty directories cannot be checked out into to address

I would also be afraid that it now allows writing non-empty directories, wiping data in the process.

It might already exist as well, in which case you can point it out to me.

@j-walther

Jan Walther (j-walther) commented Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

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.

@Byron Sebastian Thiel (Byron) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 = true in PrepareFetch::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: true still 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.

Comment thread gix/src/clone/mod.rs
@j-walther

Copy link
Copy Markdown
Contributor Author

One problem remains: I don't understand what the PR is trying to fix.

Before my changes every code path set the destination_must_be_empty flag to true in a part of the code the user couldn't influence so you could never check out into a non-empty directory. This made the flag essentially meaningless since it'd effectively always be true. This PR removes the line that forces it to always be true so the user can actually opt into checking out into non-empty directories.

@j-walther

Copy link
Copy Markdown
Contributor Author

I've updated the tests to better reflect the change and made it so destination_must_be_empty is true by default.

@j-walther Jan Walther (j-walther) changed the title Allow checkouts of empty repositories Allow checkouts of repositories into non-empty directories Apr 13, 2026
Comment thread gix/src/lib.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment thread gix/src/clone/mod.rs Outdated
Comment thread gix/src/create.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

Comment thread gix/src/create.rs Outdated
Comment thread gix/src/clone/mod.rs
Comment thread gix/tests/gix/clone.rs
Comment thread gix/src/create.rs Outdated
Comment thread gix/src/create.rs
Matthew Dowell (Mbd06b) added a commit to ethosengine/brit that referenced this pull request Apr 25, 2026
## Summary

Closes 9 compat-mode rows in tests/journey/parity/branch.sh, lands three
reusable foundations consumed by future sprints (commit, push, tag, merge),
and folds in 6 upstream syncs that arrived during the sprint.

## Closed parity rows (all bytes-mode, steward-verified, on-disk state asserted)

- `-v` / `--verbose` — column-aligned name + SHA + subject
- `-vv` — adds `[upstream: ahead N, behind N]` tracking annotation
- `--abbrev=N` / `--no-abbrev` — SHA width override
- `--column=always` — column-major packing
- `-u` / `--set-upstream-to` — writes branch.<n>.{remote,merge}
- `--unset-upstream` — clears them; emits `fatal: branch '<n>' has no upstream information` + exit 128 when absent
- `--track` (create-side) — writes upstream config after ref creation
- `--edit-description` — spawns editor, writes branch.<n>.description

Plus three on-disk-state guard rows (chained gix → gix invocations) so
bytes-mode parity tests can no longer mask missing-side-effect regressions
in branch-config writers.

## Foundations introduced

- gix::Repository::{set_branch_upstream, unset_branch_upstream, set_branch_description}
  with $GIT_DIR/config persistence via gix_config::File::write_to_filter
  on Source::Local sections (matches gix/src/clone/fetch/util.rs::setup_branch_config).
- Branch::DESCRIPTION key constant + SnapshotMut::clear_subsection_value helper.
- gitoxide-core::shared::columns::write_columns — column-major packer (reusable
  for status --column).
- gitoxide-core::shared::editor::edit_file — $GIT_EDITOR/core.editor/$VISUAL/$EDITOR/vi
  resolver + shell-spawn (reusable for commit -e, tag --edit, merge --edit).

## Upstream syncs folded in

- merge: pull upstream gix-main into gix-brit (security hardening + fuzz expansion)
- merge: pull GitoxideLabs#2508 — allow checkouts into non-empty directories
- merge: pull GitoxideLabs#2375 — filters and partial cloning: initial support
- merge: pull GitoxideLabs#2503 — IOUC (UNTR extension) dirwalk speedup
- merge: pull GitoxideLabs#2457 — gix-blame: Incremental
- merge: pull GitoxideLabs#2465 — barebones server-side upload-pack

## Adjustments

- steward agent: surface idiomatic Rust as an explicit design priority,
  reframing vendor/git as the *what* (flag surface, exit codes, output bytes)
  while gitoxide idioms anchor *how*.
- Post-merge cleanup: dropped duplicate clone --filter stub (superseded by GitoxideLabs#2375)
  and pr-2457's orphaned log helpers (our log_all already handles paths).

Plan: etc/plan/foundations-sprint-branch-render-and-config.md
Pre-squash backup tag: pre-squash-foundations

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@j-walther

Copy link
Copy Markdown
Contributor Author

Hi Sebastian Thiel (@Byron), this is rebased on top of main and addresses the open Drop-safety concern that was raised in both your review and the Copilot reviewer's follow-ups.

Changes since you last looked:

  • Drop safety fix (36f3805): PrepareFetch and PrepareCheckout now track whether the destination directory was empty at construction time. On drop, only the .git directory we created is removed when the destination held pre-existing user files — remove_dir_all(workdir) is reserved for the empty-destination case. This directly addresses your concern that opting in to non-empty destinations could otherwise wipe data on a failed clone.
  • Regression test for that exact failure path (drop_after_failed_fetch_into_non_empty_directory_preserves_pre_existing_files in gix/tests/gix/clone.rs) — I verified it fails without the Drop fix and passes with it.
  • Field doc updated on Options::destination_must_be_empty to spell out the None / Some(true) / Some(false) semantics.
  • Rebased onto main (clean except for two small test-file conflicts already resolved).

The boolOption<bool> change is intentional and the cleanest way I found to let gix::init keep its documented permissive default while prepare_clone stays strict by default. Happy to revisit the API shape if you'd prefer a different split.

CI is green.

Also make turn `destination_must_be_empty` into `Option<bool>`

@Byron Sebastian Thiel (Byron) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR got lucky as I nerd-sniped myself into probably putting more time in it as it would have cost me if I would have implemented this feature from the start.

Nowadays, I think it costs more to lift a mediocre PR into what's needed than to do it from scratch given agent support, and it's something I will consider in future, which probably leans more towards closing such PRs than engaging with it.

Lesson learned, I hope.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.

Comment thread gix/src/clone/mod.rs Outdated
Comment thread gix/src/create.rs Outdated
Comment thread gix/src/init.rs Outdated
Comment thread gix/tests/gix/clone.rs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Comment thread gix/src/clone/mod.rs Outdated
Co-authored-by: Sebastian Thiel <[email protected]>
@Byron
Sebastian Thiel (Byron) merged commit 9013c3f into GitoxideLabs:main May 12, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants