Skip to content

Commit

Permalink
fix!: create::into(…) takes create::Kind to determine if it's bar…
Browse files Browse the repository at this point in the history
…e or not. (#450)

First of all, `bare` is not an option anymore, but a parameter as
it can't be defaulted.
Other public signatures change as well to accomodate for it.
  • Loading branch information
Byron committed Oct 17, 2022
1 parent 041ede9 commit 2bece79
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 61 deletions.
6 changes: 4 additions & 2 deletions git-repository/src/clone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ impl PrepareFetch {
pub fn new<Url, E>(
url: Url,
path: impl AsRef<std::path::Path>,
create_opts: crate::create::Options,
kind: crate::create::Kind,
mut create_opts: crate::create::Options,
open_opts: crate::open::Options,
) -> Result<Self, Error>
where
Url: TryInto<git_url::Url, Error = E>,
git_url::parse::Error: From<E>,
{
let url = url.try_into().map_err(git_url::parse::Error::from)?;
let repo = crate::ThreadSafeRepository::init_opts(path, create_opts, open_opts)?.to_thread_local();
create_opts.destination_must_be_empty = true;
let repo = crate::ThreadSafeRepository::init_opts(path, kind, create_opts, open_opts)?.to_thread_local();
Ok(PrepareFetch {
url,
#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
Expand Down
60 changes: 39 additions & 21 deletions git-repository/src/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ pub enum Error {
CreateDirectory { source: std::io::Error, path: PathBuf },
}

/// The kind of repository to create.
#[derive(Debug, Copy, Clone)]
pub enum Kind {
/// An empty repository with a `.git` folder, setup to contain files in its worktree.
WithWorktree,
/// A bare repository without a worktree.
Bare,
}

const TPL_INFO_EXCLUDE: &[u8] = include_bytes!("assets/baseline-init/info/exclude");
const TPL_HOOKS_APPLYPATCH_MSG: &[u8] = include_bytes!("assets/baseline-init/hooks/applypatch-msg.sample");
const TPL_HOOKS_COMMIT_MSG: &[u8] = include_bytes!("assets/baseline-init/hooks/commit-msg.sample");
Expand Down Expand Up @@ -98,11 +107,12 @@ fn create_dir(p: &Path) -> Result<(), Error> {
}

/// Options for use in [`into()`];
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Default)]
pub struct Options {
/// If true, the repository will be a bare repository without a worktree.
pub bare: bool,

/// If true, and the kind of repository to create has a worktree, then the destination directory must be empty.
///
/// By default repos with worktree can be initialized into a non-empty repository as long as there is no `.git` directory.
pub destination_must_be_empty: bool,
/// If set, use these filesystem capabilities to populate the respective git-config fields.
/// If `None`, the directory will be probed.
pub fs_capabilities: Option<git_worktree::fs::Capabilities>,
Expand All @@ -115,25 +125,33 @@ pub struct Options {
/// that return a [Repository][crate::Repository].
pub fn into(
directory: impl Into<PathBuf>,
Options { bare, fs_capabilities }: Options,
kind: Kind,
Options {
fs_capabilities,
destination_must_be_empty,
}: Options,
) -> Result<git_discover::repository::Path, Error> {
let mut dot_git = directory.into();
if fs::read_dir(&dot_git)
.or_else(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
fs::create_dir(&dot_git).and_then(|_| fs::read_dir(&dot_git))
} else {
Err(err)
}
})
.map_err(|err| Error::IoOpen {
source: err,
path: dot_git.clone(),
})?
.count()
!= 0
{
return Err(Error::DirectoryNotEmpty { path: dot_git });
let bare = matches!(kind, Kind::Bare);

if bare || destination_must_be_empty {
if fs::read_dir(&dot_git)
.or_else(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
fs::create_dir(&dot_git).and_then(|_| fs::read_dir(&dot_git))
} else {
Err(err)
}
})
.map_err(|err| Error::IoOpen {
source: err,
path: dot_git.clone(),
})?
.count()
!= 0
{
return Err(Error::DirectoryNotEmpty { path: dot_git });
}
}

if !bare {
Expand Down
11 changes: 8 additions & 3 deletions git-repository/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ impl ThreadSafeRepository {
///
/// Fails without action if there is already a `.git` repository inside of `directory`, but
/// won't mind if the `directory` otherwise is non-empty.
pub fn init(directory: impl AsRef<Path>, options: crate::create::Options) -> Result<Self, Error> {
pub fn init(
directory: impl AsRef<Path>,
kind: crate::create::Kind,
options: crate::create::Options,
) -> Result<Self, Error> {
use git_sec::trust::DefaultForLevel;
let open_options = crate::open::Options::default_for_level(git_sec::Trust::Full);
Self::init_opts(directory, options, open_options)
Self::init_opts(directory, kind, options, open_options)
}

/// Similar to [`init`][Self::init()], but allows to determine how exactly to open the newly created repository.
Expand All @@ -51,10 +55,11 @@ impl ThreadSafeRepository {
/// configuration key.
pub fn init_opts(
directory: impl AsRef<Path>,
kind: crate::create::Kind,
create_options: crate::create::Options,
mut open_options: crate::open::Options,
) -> Result<Self, Error> {
let path = crate::create::into(directory.as_ref(), create_options)?;
let path = crate::create::into(directory.as_ref(), kind, create_options)?;
let (git_dir, worktree_dir) = path.into_repository_and_work_tree_directories();
open_options.git_dir_trust = Some(git_sec::Trust::Full);
let repo = ThreadSafeRepository::open_from_paths(git_dir, worktree_dir, open_options)?;
Expand Down
30 changes: 6 additions & 24 deletions git-repository/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,26 +200,12 @@ pub fn discover(directory: impl AsRef<std::path::Path>) -> Result<Repository, di

/// See [ThreadSafeRepository::init()], but returns a [`Repository`] instead.
pub fn init(directory: impl AsRef<std::path::Path>) -> Result<Repository, init::Error> {
ThreadSafeRepository::init(
directory,
create::Options {
bare: false,
fs_capabilities: None,
},
)
.map(Into::into)
ThreadSafeRepository::init(directory, create::Kind::WithWorktree, create::Options::default()).map(Into::into)
}

/// See [ThreadSafeRepository::init()], but returns a [`Repository`] instead.
pub fn init_bare(directory: impl AsRef<std::path::Path>) -> Result<Repository, init::Error> {
ThreadSafeRepository::init(
directory,
create::Options {
bare: true,
fs_capabilities: None,
},
)
.map(Into::into)
ThreadSafeRepository::init(directory, create::Kind::Bare, create::Options::default()).map(Into::into)
}

/// Create a platform for configuring a bare clone from `url` to the local `path`, using default options for opening it (but
Expand All @@ -237,10 +223,8 @@ where
clone::PrepareFetch::new(
url,
path,
create::Options {
bare: true,
fs_capabilities: None,
},
create::Kind::Bare,
create::Options::default(),
open_opts_with_git_binary_config(),
)
}
Expand All @@ -257,10 +241,8 @@ where
clone::PrepareFetch::new(
url,
path,
create::Options {
bare: false,
fs_capabilities: None,
},
crate::create::Kind::WithWorktree,
create::Options::default(),
open_opts_with_git_binary_config(),
)
}
Expand Down
13 changes: 13 additions & 0 deletions git-repository/tests/clone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ fn clone_and_early_persist_without_receive() -> crate::Result {
Ok(())
}

#[test]
fn clone_and_destination_must_be_empty() -> crate::Result {
let tmp = git_testtools::tempfile::TempDir::new()?;
std::fs::write(tmp.path().join("file"), b"hello")?;
match git::prepare_clone_bare(remote::repo("base").path(), tmp.path()) {
Ok(_) => unreachable!("this should fail as the directory isn't empty"),
Err(err) => assert!(err
.to_string()
.starts_with("Refusing to initialize the non-empty directory as ")),
}
Ok(())
}

#[test]
fn clone_bare_into_empty_directory_and_early_drop() -> crate::Result {
let tmp = git_testtools::tempfile::TempDir::new()?;
Expand Down
35 changes: 28 additions & 7 deletions git-repository/tests/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@ mod non_bare {
let tmp = tempfile::tempdir()?;
let repo: git::Repository = git::ThreadSafeRepository::init_opts(
tmp.path(),
git::create::Options {
bare: true,
fs_capabilities: None,
},
git::create::Kind::Bare,
git::create::Options::default(),
git::open::Options::isolated().config_overrides(Some("init.defaultBranch=special")),
)?
.into();
Expand All @@ -87,15 +85,38 @@ mod non_bare {
}

#[test]
fn init_into_non_empty_directory_is_not_allowed() -> crate::Result {
fn init_into_non_empty_directory_is_not_allowed_if_option_is_set_as_used_for_clone() -> crate::Result {
let tmp = tempfile::tempdir()?;
std::fs::write(tmp.path().join("existing.txt"), b"I was here before you")?;

assert!(git_repository::init(tmp.path())
.unwrap_err()
let err = git::ThreadSafeRepository::init_opts(
tmp.path(),
git::create::Kind::WithWorktree,
git::create::Options {
destination_must_be_empty: true,
..Default::default()
},
git::open::Options::isolated(),
)
.unwrap_err();
assert!(err
.to_string()
.starts_with("Refusing to initialize the non-empty directory as"));
Ok(())
}

#[test]
fn init_into_non_empty_directory_is_allowed_by_default() -> crate::Result {
let tmp = tempfile::tempdir()?;
std::fs::write(tmp.path().join("existing.txt"), b"I was here before you")?;

let repo = git_repository::init(tmp.path())?;
assert_eq!(repo.work_dir().expect("present"), tmp.path());
assert_eq!(
repo.git_dir(),
tmp.path().join(".git"),
"gitdir is inside of the workdir"
);
Ok(())
}
}
6 changes: 2 additions & 4 deletions git-repository/tests/repository/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,8 @@ fn empty_bare_repo() -> crate::Result<(tempfile::TempDir, git::Repository)> {
let tmp = tempfile::tempdir()?;
let repo = git::ThreadSafeRepository::init_opts(
tmp.path(),
git::create::Options {
bare: true,
fs_capabilities: None,
},
git::create::Kind::Bare,
git::create::Options::default(),
git::open::Options::isolated(),
)?
.into();
Expand Down

0 comments on commit 2bece79

Please sign in to comment.