Implement GitRepository::create
Good to know before start
To create a new repository, we start with a directory (which we create if doesn’t already exist) and create the git directory inside (which must not exist already, or be empty). That directory is called .git (the leading period makes it “hidden” on Unix systems), and contains:
git/objects/
: the object store, which we’ll introduce in the next section.git/refs/
the reference store, which we’ll discuss a bit later. It contains two subdirectories,heads
andtags
.git/HEAD
, a reference to the current HEAD (more on that later!)git/config
, the repository’s configuration file.git/description
, holds a free-form description of this repository’s contents, for humans, and is rarely used.
Implementation
Implement Default
trait for GitConfig:
// src/git_config.rs
impl GitConfig {
//...
pub fn default_str() -> &'static str {
r#"[core]
bare = false
repositoryformatversion = 0
filemode = false"#
}
//...
}
impl Default for GitConfig {
fn default() -> Self {
GitConfig::default_str().parse().unwrap()
}
}
Add a function to DirectoryManager
to create initial directory tree:
// src/directory_manager.rs
impl DirectoryManager {
// ...
pub fn create_directory_tree(&self) -> Result<(), std::io::Error> {
fs::create_dir_all(&self.work_tree)?;
fs::create_dir_all(&self.dot_git_path)?;
fs::create_dir_all(self.dot_git_path.join("branches"))?;
fs::create_dir_all(self.dot_git_path.join("objects"))?;
fs::create_dir_all(self.dot_git_path.join("refs").join("tags"))?;
fs::create_dir_all(self.dot_git_path.join("refs").join("heads"))?;
Ok(())
}
// ...
}
and let's keep the common paths in the DirectoryManager
:
#[derive(Debug)]
pub struct DirectoryManager {
//...
pub config_file: PathBuf,
pub description_file: PathBuf,
pub head_file: PathBuf,
}
impl DirectoryManager {
pub fn new<T: Into<PathBuf>>(base_path: T) -> Self {
let base_path: PathBuf = base_path.into();
let dot_git_path = base_path.join(".git");
Self {
work_tree: base_path,
config_file: dot_git_path.join("config"),
description_file: dot_git_path.join("description"),
head_file: dot_git_path.join("HEAD"),
dot_git_path,
}
}
}
pay attention that config_file
function is removed. Add an auxiliary function to DirectoryManager
to check if .git
is empty:
pub fn is_dot_git_empty(&self) -> Result<bool, std::io::Error> {
Ok(!self.dot_git_path.exists() || self.dot_git_path.read_dir()?.next().is_none())
}
Update tests:
// src/directory_manager.rs
#[test]
fn should_return_correct_file_paths() {
let dir_manager = DirectoryManager::new(PROJECT_DIR);
assert_eq!(
dir_manager.config_file,
Path::new("~/home/projects/test/.git/config")
);
assert_eq!(
dir_manager.description_file,
Path::new("~/home/projects/test/.git/description")
);
assert_eq!(
dir_manager.head_file,
Path::new("~/home/projects/test/.git/HEAD")
);
}
Now let's implement GitRepository::create
function:
// src/repository.rs
pub fn create(base_path: &Path) -> Result<Self, String> {
let directory_manager = DirectoryManager::new(base_path);
// First, check if the work tree exists and if it does, is it a directory?
if directory_manager.work_tree.exists() && !directory_manager.work_tree.is_dir() {
return Err(format!("{} is not a directory!", base_path.display()));
}
// Then check if .git directory is empty?
if !directory_manager
.is_dot_git_empty()
.map_err(|e| e.to_string())?
{
return Err(format!(
"{} is not empty!",
directory_manager.dot_git_path.display()
));
}
// Create the initial directory tree, .git, .git/refs, .git/objects, etc.
directory_manager
.create_directory_tree()
.map_err(|e| e.to_string())?;
// Write initial contents of .git/description
std::fs::write(
&directory_manager.description_file,
"Unnamed repository; edit this file 'description' to name the repository.\n",
)
.map_err(|e| e.to_string())?;
// Write initial contents of .git/HEAD
std::fs::write(&directory_manager.head_file, "ref: refs/heads/master\n")
.map_err(|e| e.to_string())?;
// Write initial contents of .git/config
std::fs::write(&directory_manager.config_file, GitConfig::default_str())
.map_err(|e| e.to_string())?;
Ok(Self {
directory_manager,
config: GitConfig::default(),
})
}
Last updated