mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-15 16:53:25 +00:00
a8a9f7dedd
It's annoying to have to have the Git repo and Jujube repo in separate directories. This commit adds `jj init --git`, which creates a new Jujube repo with an empty, bare git repo in `.jj/git/`. Hopefully the `jj git` subcommands will eventually provide enough functionality for working with the Git repo that the user won't have to use Git commands directly. If they still do, they can run them from inside `.jj/git/`, or create a new worktree based on that bare repo. The implementation is quite straight-forward. One thing to note is that I made `.jj/store` support relative paths to the Git repo. That's mostly so the Jujube repo can be moved around freely.
144 lines
5.3 KiB
Rust
144 lines
5.3 KiB
Rust
// Copyright 2020 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use git2::Oid;
|
|
use jj_lib::commit::Commit;
|
|
use jj_lib::git;
|
|
use jj_lib::git::GitPushError;
|
|
use jj_lib::repo::ReadonlyRepo;
|
|
use jj_lib::settings::UserSettings;
|
|
use jj_lib::store::CommitId;
|
|
use jj_lib::testutils;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::Arc;
|
|
use tempfile::TempDir;
|
|
|
|
/// Create a Git repo with a single commit in the "main" branch.
|
|
fn create_source_repo(dir: &Path) -> CommitId {
|
|
let git_repo = git2::Repository::init_bare(dir).unwrap();
|
|
let signature = git2::Signature::now("Someone", "someone@example.com").unwrap();
|
|
let empty_tree_id = Oid::from_str("4b825dc642cb6eb9a060e54bf8d69288fbee4904").unwrap();
|
|
let empty_tree = git_repo.find_tree(empty_tree_id).unwrap();
|
|
let oid = git_repo
|
|
.commit(
|
|
Some("refs/heads/main"),
|
|
&signature,
|
|
&signature,
|
|
"message",
|
|
&empty_tree,
|
|
&[],
|
|
)
|
|
.unwrap();
|
|
CommitId(oid.as_bytes().to_vec())
|
|
}
|
|
|
|
fn create_repo_clone(source: &Path, destination: &Path) {
|
|
git2::Repository::clone(&source.to_str().unwrap(), destination).unwrap();
|
|
}
|
|
|
|
struct PushTestSetup {
|
|
source_repo_dir: PathBuf,
|
|
clone_repo_dir: PathBuf,
|
|
jj_repo: Arc<ReadonlyRepo>,
|
|
new_commit: Commit,
|
|
}
|
|
|
|
fn set_up_push_repos(settings: &UserSettings, temp_dir: &TempDir) -> PushTestSetup {
|
|
let source_repo_dir = temp_dir.path().join("source");
|
|
let clone_repo_dir = temp_dir.path().join("clone");
|
|
let jj_repo_dir = temp_dir.path().join("jj");
|
|
let initial_commit_id = create_source_repo(&source_repo_dir);
|
|
create_repo_clone(&source_repo_dir, &clone_repo_dir);
|
|
std::fs::create_dir(&jj_repo_dir).unwrap();
|
|
let mut jj_repo =
|
|
ReadonlyRepo::init_external_git(&settings, jj_repo_dir.clone(), clone_repo_dir.clone());
|
|
let new_commit = testutils::create_random_commit(&settings, &jj_repo)
|
|
.set_parents(vec![initial_commit_id.clone()])
|
|
.write_to_new_transaction(&jj_repo, "test");
|
|
Arc::get_mut(&mut jj_repo).unwrap().reload();
|
|
PushTestSetup {
|
|
source_repo_dir,
|
|
clone_repo_dir,
|
|
jj_repo,
|
|
new_commit,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_commit_success() {
|
|
let settings = testutils::user_settings();
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
|
let setup = set_up_push_repos(&settings, &temp_dir);
|
|
let result = git::push_commit(&setup.new_commit, "origin", "main");
|
|
assert_eq!(result, Ok(()));
|
|
|
|
// Check that the ref got updated in the source repo
|
|
let source_repo = git2::Repository::open(&setup.source_repo_dir).unwrap();
|
|
let new_target = source_repo
|
|
.find_reference("refs/heads/main")
|
|
.unwrap()
|
|
.target();
|
|
let new_oid = Oid::from_bytes(&setup.new_commit.id().0).unwrap();
|
|
assert_eq!(new_target, Some(new_oid));
|
|
|
|
// Check that the ref got updated in the cloned repo. This just tests our
|
|
// assumptions about libgit2 because we want the refs/remotes/origin/main
|
|
// branch to be updated.
|
|
let clone_repo = git2::Repository::open(&setup.clone_repo_dir).unwrap();
|
|
let new_target = clone_repo
|
|
.find_reference("refs/remotes/origin/main")
|
|
.unwrap()
|
|
.target();
|
|
assert_eq!(new_target, Some(new_oid));
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_commit_not_fast_forward() {
|
|
let settings = testutils::user_settings();
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
|
let mut jj_repo = set_up_push_repos(&settings, &temp_dir).jj_repo;
|
|
let new_commit = testutils::create_random_commit(&settings, &jj_repo)
|
|
.write_to_new_transaction(&jj_repo, "test");
|
|
Arc::get_mut(&mut jj_repo).unwrap().reload();
|
|
let result = git::push_commit(&new_commit, "origin", "main");
|
|
assert_eq!(result, Err(GitPushError::NotFastForward));
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_commit_no_such_remote() {
|
|
let settings = testutils::user_settings();
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
|
let setup = set_up_push_repos(&settings, &temp_dir);
|
|
let result = git::push_commit(&setup.new_commit, "invalid-remote", "main");
|
|
assert_eq!(result, Err(GitPushError::NoSuchRemote));
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_commit_invalid_remote() {
|
|
let settings = testutils::user_settings();
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
|
let setup = set_up_push_repos(&settings, &temp_dir);
|
|
let result = git::push_commit(&setup.new_commit, "http://invalid-remote", "main");
|
|
assert_eq!(result, Err(GitPushError::NoSuchRemote));
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_commit_non_git() {
|
|
let settings = testutils::user_settings();
|
|
let (_temp_dir, repo) = testutils::init_repo(&settings, false);
|
|
let commit =
|
|
testutils::create_random_commit(&settings, &repo).write_to_new_transaction(&repo, "test");
|
|
let result = git::push_commit(&commit, "origin", "main");
|
|
assert_eq!(result, Err(GitPushError::NotAGitRepo));
|
|
}
|