[go: up one dir, main page]

gix/
init.rs

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
12/// The name of the branch to use if non is configured via git configuration.
13///
14/// # Deviation
15///
16/// We use `main` instead of `master`.
17pub const DEFAULT_BRANCH_NAME: &str = "main";
18
19/// The error returned by [`crate::init()`].
20#[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    /// Create a repository with work-tree within `directory`, creating intermediate directories as needed.
40    ///
41    /// Fails without action if there is already a `.git` repository inside of `directory`, but
42    /// won't mind if the `directory` otherwise is non-empty.
43    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    /// Similar to [`init`][Self::init()], but allows to determine how exactly to open the newly created repository.
54    ///
55    /// # Deviation
56    ///
57    /// Instead of naming the default branch `master`, we name it `main` unless configured explicitly using the `init.defaultBranch`
58    /// configuration key.
59    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        // The repo will use `core.precomposeUnicode` to adjust the value as needed.
69        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}