workspace: add function that initializes colocated git repository

One less git2 API use in CLI.

The function name GitBackend::init_colocated() is a bit odd, but we need to
specify the work-tree path, not the ".git" repo path. So we can't eliminate
the notion of the working copy path anyway.
This commit is contained in:
Yuya Nishihara 2023-11-03 21:30:08 +09:00
parent 77e16243d6
commit 602b44258e
4 changed files with 75 additions and 2 deletions

View file

@ -519,8 +519,7 @@ fn do_git_clone(
wc_path: &Path,
) -> Result<(WorkspaceCommandHelper, GitFetchStats), CommandError> {
let (workspace, repo) = if colocate {
let git_repo = git2::Repository::init(wc_path)?;
Workspace::init_external_git(command.settings(), wc_path, git_repo.path())?
Workspace::init_colocated_git(command.settings(), wc_path)?
} else {
Workspace::init_internal_git(command.settings(), wc_path)?
};

View file

@ -136,6 +136,29 @@ impl GitBackend {
Self::init_with_repo(store_path, git_repo_path, git_repo)
}
/// Initializes backend by creating a new Git repo at the specified
/// workspace path. The workspace directory must exist.
pub fn init_colocated(
store_path: &Path,
workspace_root: &Path,
) -> Result<Self, Box<GitBackendInitError>> {
let canonical_workspace_root = {
let path = store_path.join(workspace_root);
path.canonicalize()
.context(&path)
.map_err(GitBackendInitError::Path)?
};
let git_repo = gix::ThreadSafeRepository::init(
canonical_workspace_root,
gix::create::Kind::WithWorktree,
gix::create::Options::default(),
)
.map_err(GitBackendInitError::InitRepository)?;
let git_repo_path = workspace_root.join(".git");
Self::init_with_repo(store_path, &git_repo_path, git_repo)
}
/// Initializes backend with an existing Git repo at the specified path.
pub fn init_external(
store_path: &Path,
git_repo_path: &Path,

View file

@ -160,6 +160,32 @@ impl Workspace {
Self::init_with_backend(user_settings, workspace_root, backend_initializer)
}
/// Initializes a workspace with a new Git backend and Git repo that shares
/// the same working copy.
pub fn init_colocated_git(
user_settings: &UserSettings,
workspace_root: &Path,
) -> Result<(Self, Arc<ReadonlyRepo>), WorkspaceInitError> {
let backend_initializer = {
let workspace_root = workspace_root.to_owned();
move |store_path: &Path| -> Result<Box<dyn Backend>, BackendInitError> {
// TODO: Clean up path normalization. store_path is canonicalized by
// ReadonlyRepo::init(). workspace_root will be canonicalized by
// Workspace::new(), but it's not yet here.
let store_relative_workspace_root =
if let Ok(workspace_root) = workspace_root.canonicalize() {
file_util::relative_path(store_path, &workspace_root)
} else {
workspace_root.to_owned()
};
let backend =
GitBackend::init_colocated(store_path, &store_relative_workspace_root)?;
Ok(Box::new(backend))
}
};
Self::init_with_backend(user_settings, workspace_root, &backend_initializer)
}
/// Initializes a workspace with an existing Git repo at the specified path.
pub fn init_external_git(
user_settings: &UserSettings,

View file

@ -75,6 +75,31 @@ fn test_init_internal_git() {
write_random_commit(tx.mut_repo(), &settings);
}
#[test]
fn test_init_colocated_git() {
let settings = testutils::user_settings();
let temp_dir = testutils::new_temp_dir();
let (canonical, uncanonical) = canonicalize(temp_dir.path());
let (workspace, repo) = Workspace::init_colocated_git(&settings, &uncanonical).unwrap();
let git_backend = repo
.store()
.backend_impl()
.downcast_ref::<GitBackend>()
.unwrap();
assert_eq!(repo.repo_path(), &canonical.join(".jj").join("repo"));
assert_eq!(workspace.workspace_root(), &canonical);
assert_eq!(git_backend.git_repo_path(), canonical.join(".git"));
assert_eq!(git_backend.git_workdir(), Some(canonical.as_ref()));
assert_eq!(
std::fs::read_to_string(repo.repo_path().join("store").join("git_target")).unwrap(),
"../../../.git"
);
// Just test that we can write a commit to the store
let mut tx = repo.start_transaction(&settings, "test");
write_random_commit(tx.mut_repo(), &settings);
}
#[test]
fn test_init_external_git() {
let settings = testutils::user_settings();