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