diff --git a/git-cliff-core/src/repo.rs b/git-cliff-core/src/repo.rs index 81a930b996..bcd55e048c 100644 --- a/git-cliff-core/src/repo.rs +++ b/git-cliff-core/src/repo.rs @@ -48,7 +48,15 @@ impl Repository { /// Initializes (opens) the repository. pub fn init(path: PathBuf) -> Result { if path.exists() { - let inner = GitRepository::open(&path)?; + let inner = GitRepository::open(&path).or_else(|err| { + let jujutsu_path = + path.join(".jj").join("repo").join("store").join("git"); + if jujutsu_path.exists() { + GitRepository::open_bare(&jujutsu_path) + } else { + Err(err) + } + })?; let changed_files_cache_path = inner .path() .join(env!("CARGO_PKG_NAME")) @@ -503,9 +511,12 @@ fn ssh_path_segments(url: &str) -> Result { mod test { use super::*; use crate::commit::Commit as AppCommit; - use std::env; use std::process::Command; use std::str; + use std::{ + env, + fs, + }; use temp_dir::TempDir; fn get_last_commit_hash() -> Result { @@ -735,6 +746,62 @@ mod test { (repo, temp_dir) } + #[test] + fn open_jujutsu_repo() { + let (repo, _temp_dir) = create_temp_repo(); + // working copy is the directory that contains the .git directory: + let working_copy = repo.path; + + // Make the Git repository bare and set HEAD + std::process::Command::new("git") + .args(["config", "core.bare", "true"]) + .current_dir(&working_copy) + .status() + .expect("failed to make git repo non-bare"); + // Move the Git repo into jj + let store = working_copy.join(".jj").join("repo").join("store"); + fs::create_dir_all(&store).expect("failed to create dir"); + fs::rename(working_copy.join(".git"), store.join("git")) + .expect("failed to move git repo"); + + // Open repo from working copy, that contains the .jj directory + let repo = Repository::init(working_copy).expect("failed to init repo"); + + // macOS canonical path for temp directories is in /private + // libgit2 forces the path to be canonical regardless of what we pass in + if repo.inner.path().starts_with("/private") { + assert_eq!( + repo.inner.path().strip_prefix("/private"), + store.join("git").strip_prefix("/"), + "open git repo in .jj/repo/store/" + ); + } else { + assert_eq!( + repo.inner.path(), + store.join("git"), + "open git repo in .jj/repo/store/" + ); + } + } + + #[test] + fn propagate_error_if_no_repo_found() { + let temp_dir = + TempDir::with_prefix("git-cliff-").expect("failed to create temp dir"); + + let path = temp_dir.path().to_path_buf(); + + let result = Repository::init(path.clone()); + + assert!(result.is_err()); + if let Err(error) = result { + assert!(format!("{error:?}").contains( + format!("could not find repository at '{}'", path.display()) + .as_str() + )) + } + } + fn create_commit_with_files<'a>( repo: &'a Repository, files: Vec<(&'a str, &'a str)>, diff --git a/website/docs/usage/jujutsu.md b/website/docs/usage/jujutsu.md new file mode 100644 index 0000000000..29decbdbdf --- /dev/null +++ b/website/docs/usage/jujutsu.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 12 +--- + +# Jujutsu + +You can use with a repository that has been cloned using [jujutsu](https://martinvonz.github.io/jj/latest/). + +## Colocated + +If the repository was cloned by `jujutsu` using the `--colocate` option, then all you need to do is make sure that +you have checked out your mainline branch using git. +If you don't, then you will likely see an error about an unborn branch. + +## Non-colocated + +If the repository was cloned by `jujutsu` but *not* using the `--colocate` option, +then the Git repository, normally the `.git` directory, is located in `.jj/repo/store/git` + +Create a file in the root of your repository that tells Git, and `git-cliff` where the Git repository is +and update the `HEAD` to point to your main remote branch: + +e.g.: + +```bash +jj git clone https://github.com/orhun/menyoki +cd menyoki +echo "gitdir: .jj/repo/store/git" > .git +echo "ref: refs/remotes/origin/master" > .jj/repo/store/git/HEAD +``` + +N.B.: Replace `master` in the last command with the name of your main remote branch. e.g. `main`, `trunk`, etc. +