repo: make .jj/store a directory also in Git-backed repos

I think this is just cleaner, and it gives us room to put other
store-related data in the `.jj/store/` directory. I may want to use
that place for writing the metadata we currently write in Git notes
(#7).
This commit is contained in:
Martin von Zweigbergk 2021-10-13 22:50:50 -07:00
parent dba72af857
commit 1b6efdc3f8
3 changed files with 58 additions and 38 deletions

View File

@ -16,7 +16,7 @@ use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::{Read, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
@ -141,9 +141,7 @@ impl ReadonlyRepo {
wc_path: PathBuf, wc_path: PathBuf,
) -> Result<Arc<ReadonlyRepo>, RepoInitError> { ) -> Result<Arc<ReadonlyRepo>, RepoInitError> {
let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?; let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?;
let store_path = repo_path.join("store"); let backend = Box::new(LocalBackend::init(repo_path.join("store")));
fs::create_dir(&store_path).unwrap();
let backend = Box::new(LocalBackend::init(store_path));
Ok(ReadonlyRepo::init(settings, repo_path, wc_path, backend)) Ok(ReadonlyRepo::init(settings, repo_path, wc_path, backend))
} }
@ -153,13 +151,12 @@ impl ReadonlyRepo {
wc_path: PathBuf, wc_path: PathBuf,
) -> Result<Arc<ReadonlyRepo>, RepoInitError> { ) -> Result<Arc<ReadonlyRepo>, RepoInitError> {
let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?; let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?;
let git_store_path = repo_path.join("git");
git2::Repository::init_bare(&git_store_path).unwrap();
let store_path = repo_path.join("store"); let store_path = repo_path.join("store");
let git_store_path = fs::canonicalize(git_store_path).unwrap(); let git_repo_path = store_path.join("git");
let mut store_file = File::create(store_path).unwrap(); git2::Repository::init_bare(&git_repo_path).unwrap();
store_file.write_all(b"git: git").unwrap(); let mut git_target_file = File::create(store_path.join("git_target")).unwrap();
let backend = Box::new(GitBackend::load(&git_store_path)); git_target_file.write_all(b"git").unwrap();
let backend = Box::new(GitBackend::load(&git_repo_path));
Ok(ReadonlyRepo::init(settings, repo_path, wc_path, backend)) Ok(ReadonlyRepo::init(settings, repo_path, wc_path, backend))
} }
@ -171,12 +168,12 @@ impl ReadonlyRepo {
) -> Result<Arc<ReadonlyRepo>, RepoInitError> { ) -> Result<Arc<ReadonlyRepo>, RepoInitError> {
let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?; let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?;
let store_path = repo_path.join("store"); let store_path = repo_path.join("store");
let git_store_path = fs::canonicalize(git_store_path).unwrap(); let git_repo_path = fs::canonicalize(git_store_path).unwrap();
let mut store_file = File::create(store_path).unwrap(); let mut git_target_file = File::create(store_path.join("git_target")).unwrap();
store_file git_target_file
.write_all(format!("git: {}", git_store_path.to_str().unwrap()).as_bytes()) .write_all(git_repo_path.to_str().unwrap().as_bytes())
.unwrap(); .unwrap();
let backend = Box::new(GitBackend::load(&git_store_path)); let backend = Box::new(GitBackend::load(&git_repo_path));
Ok(ReadonlyRepo::init(settings, repo_path, wc_path, backend)) Ok(ReadonlyRepo::init(settings, repo_path, wc_path, backend))
} }
@ -186,6 +183,7 @@ impl ReadonlyRepo {
Err(RepoInitError::DestinationExists(repo_path)) Err(RepoInitError::DestinationExists(repo_path))
} else { } else {
fs::create_dir(&repo_path).unwrap(); fs::create_dir(&repo_path).unwrap();
fs::create_dir(repo_path.join("store")).unwrap();
fs::create_dir(repo_path.join("working_copy")).unwrap(); fs::create_dir(repo_path.join("working_copy")).unwrap();
fs::create_dir(repo_path.join("view")).unwrap(); fs::create_dir(repo_path.join("view")).unwrap();
fs::create_dir(repo_path.join("op_store")).unwrap(); fs::create_dir(repo_path.join("op_store")).unwrap();
@ -400,7 +398,26 @@ impl RepoLoader {
) -> Result<RepoLoader, RepoLoadError> { ) -> Result<RepoLoader, RepoLoadError> {
let repo_path = find_repo_dir(&wc_path).ok_or(RepoLoadError::NoRepoHere(wc_path))?; let repo_path = find_repo_dir(&wc_path).ok_or(RepoLoadError::NoRepoHere(wc_path))?;
let wc_path = repo_path.parent().unwrap().to_owned(); let wc_path = repo_path.parent().unwrap().to_owned();
let store = Store::load_store(&repo_path); let store_path = repo_path.join("store");
if store_path.is_file() {
// This is the old format. Let's be nice and upgrade any existing repos.
// TODO: Delete this in early 2022 or so
println!("The repo format has changed. Upgrading...");
let mut buf = vec![];
{
let mut store_file = File::open(&store_path).unwrap();
store_file.read_to_end(&mut buf).unwrap();
}
let contents = String::from_utf8(buf).unwrap();
assert!(contents.starts_with("git: "));
let git_backend_path_str = contents[5..].to_string();
fs::remove_file(&store_path).unwrap();
fs::create_dir(&store_path).unwrap();
fs::rename(repo_path.join("git"), store_path.join("git")).unwrap();
fs::write(store_path.join("git_target"), &git_backend_path_str).unwrap();
println!("Done. .jj/git is now .jj/store/git");
}
let store = Store::load_store(repo_path.join("store"));
let repo_settings = user_settings.with_repo(&repo_path).unwrap(); let repo_settings = user_settings.with_repo(&repo_path).unwrap();
let op_store: Arc<dyn OpStore> = Arc::new(SimpleOpStore::load(repo_path.join("op_store"))); let op_store: Arc<dyn OpStore> = Arc::new(SimpleOpStore::load(repo_path.join("op_store")));
let op_heads_store = Arc::new(OpHeadsStore::load(repo_path.join("op_heads"))); let op_heads_store = Arc::new(OpHeadsStore::load(repo_path.join("op_heads")));

View File

@ -15,7 +15,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use crate::backend; use crate::backend;
@ -51,25 +51,20 @@ impl Store {
}) })
} }
pub fn load_store(repo_path: &Path) -> Arc<Store> { pub fn load_store(store_path: PathBuf) -> Arc<Store> {
let store_path = repo_path.join("store");
let backend: Box<dyn Backend>; let backend: Box<dyn Backend>;
// TODO: Perhaps .jj/store should always be a directory. Then .jj/git would live let git_target_path = store_path.join("git_target");
// inside that directory and this function would not need to know the repo path if git_target_path.is_file() {
// (only the store path). Maybe there would be a .jj/store/format file let mut git_target_file = File::open(git_target_path).unwrap();
// indicating which kind of store it is?
if store_path.is_dir() {
backend = Box::new(LocalBackend::load(store_path));
} else {
let mut store_file = File::open(store_path).unwrap();
let mut buf = Vec::new(); let mut buf = Vec::new();
store_file.read_to_end(&mut buf).unwrap(); git_target_file.read_to_end(&mut buf).unwrap();
let contents = String::from_utf8(buf).unwrap(); let git_backend_path_str = String::from_utf8(buf).unwrap();
assert!(contents.starts_with("git: "));
let git_backend_path_str = contents[5..].to_string();
let git_backend_path = let git_backend_path =
std::fs::canonicalize(repo_path.join(PathBuf::from(git_backend_path_str))).unwrap(); std::fs::canonicalize(store_path.join(PathBuf::from(git_backend_path_str)))
.unwrap();
backend = Box::new(GitBackend::load(&git_backend_path)); backend = Box::new(GitBackend::load(&git_backend_path));
} else {
backend = Box::new(LocalBackend::load(store_path));
} }
Store::new(backend) Store::new(backend)
} }

View File

@ -23,9 +23,16 @@ fn test_init_git_internal() {
let repo_path = temp_dir.path().join("repo"); let repo_path = temp_dir.path().join("repo");
assert!(repo_path.is_dir()); assert!(repo_path.is_dir());
assert!(repo_path.join(".jj").is_dir()); assert!(repo_path.join(".jj").is_dir());
assert!(repo_path.join(".jj").join("git").is_dir()); assert!(repo_path.join(".jj").join("store").is_dir());
let store_file_contents = std::fs::read_to_string(repo_path.join(".jj").join("store")).unwrap(); assert!(repo_path.join(".jj").join("store").join("git").is_dir());
assert_eq!(store_file_contents, "git: git"); assert!(repo_path
.join(".jj")
.join("store")
.join("git_target")
.is_file());
let git_target_file_contents =
std::fs::read_to_string(repo_path.join(".jj").join("store").join("git_target")).unwrap();
assert_eq!(git_target_file_contents, "git");
assert_eq!( assert_eq!(
output.stdout_string(), output.stdout_string(),
format!("Initialized repo in \"{}\"\n", repo_path.to_str().unwrap()) format!("Initialized repo in \"{}\"\n", repo_path.to_str().unwrap())
@ -49,9 +56,10 @@ fn test_init_git_external() {
let repo_path = temp_dir.path().join("repo"); let repo_path = temp_dir.path().join("repo");
assert!(repo_path.is_dir()); assert!(repo_path.is_dir());
assert!(repo_path.join(".jj").is_dir()); assert!(repo_path.join(".jj").is_dir());
let store_file_contents = std::fs::read_to_string(repo_path.join(".jj").join("store")).unwrap(); assert!(repo_path.join(".jj").join("store").is_dir());
assert!(store_file_contents.starts_with("git: ")); let git_target_file_contents =
assert!(store_file_contents std::fs::read_to_string(repo_path.join(".jj").join("store").join("git_target")).unwrap();
assert!(git_target_file_contents
.replace('\\', "/") .replace('\\', "/")
.ends_with("/git-repo")); .ends_with("/git-repo"));
assert_eq!( assert_eq!(