diff --git a/lib/src/repo.rs b/lib/src/repo.rs index 227bec957..0c72e2a17 100644 --- a/lib/src/repo.rs +++ b/lib/src/repo.rs @@ -16,7 +16,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::{Debug, Formatter}; use std::fs; use std::fs::File; -use std::io::Write; +use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex, MutexGuard}; @@ -141,9 +141,7 @@ impl ReadonlyRepo { wc_path: PathBuf, ) -> Result, RepoInitError> { let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?; - let store_path = repo_path.join("store"); - fs::create_dir(&store_path).unwrap(); - let backend = Box::new(LocalBackend::init(store_path)); + let backend = Box::new(LocalBackend::init(repo_path.join("store"))); Ok(ReadonlyRepo::init(settings, repo_path, wc_path, backend)) } @@ -153,13 +151,12 @@ impl ReadonlyRepo { wc_path: PathBuf, ) -> Result, RepoInitError> { 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 git_store_path = fs::canonicalize(git_store_path).unwrap(); - let mut store_file = File::create(store_path).unwrap(); - store_file.write_all(b"git: git").unwrap(); - let backend = Box::new(GitBackend::load(&git_store_path)); + let git_repo_path = store_path.join("git"); + git2::Repository::init_bare(&git_repo_path).unwrap(); + let mut git_target_file = File::create(store_path.join("git_target")).unwrap(); + 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)) } @@ -171,12 +168,12 @@ impl ReadonlyRepo { ) -> Result, RepoInitError> { let repo_path = ReadonlyRepo::init_repo_dir(&wc_path)?; let store_path = repo_path.join("store"); - let git_store_path = fs::canonicalize(git_store_path).unwrap(); - let mut store_file = File::create(store_path).unwrap(); - store_file - .write_all(format!("git: {}", git_store_path.to_str().unwrap()).as_bytes()) + let git_repo_path = fs::canonicalize(git_store_path).unwrap(); + let mut git_target_file = File::create(store_path.join("git_target")).unwrap(); + git_target_file + .write_all(git_repo_path.to_str().unwrap().as_bytes()) .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)) } @@ -186,6 +183,7 @@ impl ReadonlyRepo { Err(RepoInitError::DestinationExists(repo_path)) } else { 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("view")).unwrap(); fs::create_dir(repo_path.join("op_store")).unwrap(); @@ -400,7 +398,26 @@ impl RepoLoader { ) -> Result { let repo_path = find_repo_dir(&wc_path).ok_or(RepoLoadError::NoRepoHere(wc_path))?; 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 op_store: Arc = Arc::new(SimpleOpStore::load(repo_path.join("op_store"))); let op_heads_store = Arc::new(OpHeadsStore::load(repo_path.join("op_heads"))); diff --git a/lib/src/store.rs b/lib/src/store.rs index bd671eb6a..6e0a49494 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -15,7 +15,7 @@ use std::collections::HashMap; use std::fs::File; use std::io::Read; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::{Arc, RwLock}; use crate::backend; @@ -51,25 +51,20 @@ impl Store { }) } - pub fn load_store(repo_path: &Path) -> Arc { - let store_path = repo_path.join("store"); + pub fn load_store(store_path: PathBuf) -> Arc { let backend: Box; - // TODO: Perhaps .jj/store should always be a directory. Then .jj/git would live - // inside that directory and this function would not need to know the repo path - // (only the store path). Maybe there would be a .jj/store/format file - // 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 git_target_path = store_path.join("git_target"); + if git_target_path.is_file() { + let mut git_target_file = File::open(git_target_path).unwrap(); let mut buf = Vec::new(); - 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(); + git_target_file.read_to_end(&mut buf).unwrap(); + let git_backend_path_str = String::from_utf8(buf).unwrap(); 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)); + } else { + backend = Box::new(LocalBackend::load(store_path)); } Store::new(backend) } diff --git a/tests/test_init_command.rs b/tests/test_init_command.rs index 60919b5fb..cbfb3573d 100644 --- a/tests/test_init_command.rs +++ b/tests/test_init_command.rs @@ -23,9 +23,16 @@ fn test_init_git_internal() { let repo_path = temp_dir.path().join("repo"); assert!(repo_path.is_dir()); assert!(repo_path.join(".jj").is_dir()); - assert!(repo_path.join(".jj").join("git").is_dir()); - let store_file_contents = std::fs::read_to_string(repo_path.join(".jj").join("store")).unwrap(); - assert_eq!(store_file_contents, "git: git"); + assert!(repo_path.join(".jj").join("store").is_dir()); + assert!(repo_path.join(".jj").join("store").join("git").is_dir()); + 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!( output.stdout_string(), 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"); assert!(repo_path.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!(store_file_contents.starts_with("git: ")); - assert!(store_file_contents + assert!(repo_path.join(".jj").join("store").is_dir()); + let git_target_file_contents = + std::fs::read_to_string(repo_path.join(".jj").join("store").join("git_target")).unwrap(); + assert!(git_target_file_contents .replace('\\', "/") .ends_with("/git-repo")); assert_eq!(