1#![allow(clippy::result_large_err)]
2use std::{borrow::Cow, path::Path};
3
4use gix_ref::{
5 store::WriteReflog,
6 transaction::{PreviousValue, RefEdit},
7 FullName, Target,
8};
9
10use crate::{bstr::BString, config::tree::Init, ThreadSafeRepository};
11
12pub const DEFAULT_BRANCH_NAME: &str = "main";
18
19#[derive(Debug, thiserror::Error)]
21#[allow(missing_docs)]
22pub enum Error {
23 #[error("Could not obtain the current directory")]
24 CurrentDir(#[from] std::io::Error),
25 #[error(transparent)]
26 Init(#[from] crate::create::Error),
27 #[error(transparent)]
28 Open(#[from] crate::open::Error),
29 #[error("Invalid default branch name: {name:?}")]
30 InvalidBranchName {
31 name: BString,
32 source: gix_validate::reference::name::Error,
33 },
34 #[error("Could not edit HEAD reference with new default name")]
35 EditHeadForDefaultBranch(#[from] crate::reference::edit::Error),
36}
37
38impl ThreadSafeRepository {
39 pub fn init(
44 directory: impl AsRef<Path>,
45 kind: crate::create::Kind,
46 options: crate::create::Options,
47 ) -> Result<Self, Error> {
48 use gix_sec::trust::DefaultForLevel;
49 let open_options = crate::open::Options::default_for_level(gix_sec::Trust::Full);
50 Self::init_opts(directory, kind, options, open_options)
51 }
52
53 pub fn init_opts(
60 directory: impl AsRef<Path>,
61 kind: crate::create::Kind,
62 create_options: crate::create::Options,
63 mut open_options: crate::open::Options,
64 ) -> Result<Self, Error> {
65 let path = crate::create::into(directory.as_ref(), kind, create_options)?;
66 let (git_dir, worktree_dir) = path.into_repository_and_work_tree_directories();
67 open_options.git_dir_trust = Some(gix_sec::Trust::Full);
68 open_options.current_dir = gix_fs::current_dir(false)?.into();
70 let repo = ThreadSafeRepository::open_from_paths(git_dir, worktree_dir, open_options)?;
71
72 let branch_name = repo
73 .config
74 .resolved
75 .string(Init::DEFAULT_BRANCH)
76 .unwrap_or_else(|| Cow::Borrowed(DEFAULT_BRANCH_NAME.into()));
77 if branch_name.as_ref() != DEFAULT_BRANCH_NAME {
78 let sym_ref: FullName =
79 format!("refs/heads/{branch_name}")
80 .try_into()
81 .map_err(|err| Error::InvalidBranchName {
82 name: branch_name.into_owned(),
83 source: err,
84 })?;
85 let mut repo = repo.to_thread_local();
86 let prev_write_reflog = repo.refs.write_reflog;
87 repo.refs.write_reflog = WriteReflog::Disable;
88 repo.edit_reference(RefEdit {
89 change: gix_ref::transaction::Change::Update {
90 log: Default::default(),
91 expected: PreviousValue::Any,
92 new: Target::Symbolic(sym_ref),
93 },
94 name: "HEAD".try_into().expect("valid"),
95 deref: false,
96 })?;
97 repo.refs.write_reflog = prev_write_reflog;
98 }
99
100 Ok(repo)
101 }
102}