#![allow(clippy::result_large_err)] use std::{borrow::Cow, convert::TryInto, path::Path}; use gix_macros::momo; use gix_ref::{ store::WriteReflog, transaction::{PreviousValue, RefEdit}, FullName, Target, }; use crate::{bstr::BString, config::tree::Init, ThreadSafeRepository}; /// The name of the branch to use if non is configured via git configuration. /// /// # Deviation /// /// We use `main` instead of `master`. pub const DEFAULT_BRANCH_NAME: &str = "main"; /// The error returned by [`crate::init()`]. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[error("Could not obtain the current directory")] CurrentDir(#[from] std::io::Error), #[error(transparent)] Init(#[from] crate::create::Error), #[error(transparent)] Open(#[from] crate::open::Error), #[error("Invalid default branch name: {name:?}")] InvalidBranchName { name: BString, source: gix_validate::reference::name::Error, }, #[error("Could not edit HEAD reference with new default name")] EditHeadForDefaultBranch(#[from] crate::reference::edit::Error), } impl ThreadSafeRepository { /// Create a repository with work-tree within `directory`, creating intermediate directories as needed. /// /// Fails without action if there is already a `.git` repository inside of `directory`, but /// won't mind if the `directory` otherwise is non-empty. #[momo] pub fn init( directory: impl AsRef, kind: crate::create::Kind, options: crate::create::Options, ) -> Result { use gix_sec::trust::DefaultForLevel; let open_options = crate::open::Options::default_for_level(gix_sec::Trust::Full); 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. /// /// # Deviation /// /// Instead of naming the default branch `master`, we name it `main` unless configured explicitly using the `init.defaultBranch` /// configuration key. #[momo] pub fn init_opts( directory: impl AsRef, kind: crate::create::Kind, create_options: crate::create::Options, mut open_options: crate::open::Options, ) -> Result { 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(gix_sec::Trust::Full); open_options.current_dir = std::env::current_dir()?.into(); let repo = ThreadSafeRepository::open_from_paths(git_dir, worktree_dir, open_options)?; let branch_name = repo .config .resolved .string("init", None, Init::DEFAULT_BRANCH.name) .unwrap_or_else(|| Cow::Borrowed(DEFAULT_BRANCH_NAME.into())); if branch_name.as_ref() != DEFAULT_BRANCH_NAME { let sym_ref: FullName = format!("refs/heads/{branch_name}") .try_into() .map_err(|err| Error::InvalidBranchName { name: branch_name.into_owned(), source: err, })?; let mut repo = repo.to_thread_local(); let prev_write_reflog = repo.refs.write_reflog; repo.refs.write_reflog = WriteReflog::Disable; repo.edit_reference(RefEdit { change: gix_ref::transaction::Change::Update { log: Default::default(), expected: PreviousValue::Any, new: Target::Symbolic(sym_ref), }, name: "HEAD".try_into().expect("valid"), deref: false, })?; repo.refs.write_reflog = prev_write_reflog; } Ok(repo) } }