cli: add .jj/ to .git/info/exclude when collocated

When initializing a jj repo in the same directory as its backing git
repo, add `.jj/` to `.git/info/exclude` so it doesn't show up to `git`
commands.

This is part of #44.
This commit is contained in:
Martin von Zweigbergk 2021-12-01 16:30:07 -08:00
parent e587071ffe
commit d451c1adf8
2 changed files with 64 additions and 10 deletions

View file

@ -467,7 +467,7 @@ impl MutableRepo {
self.enforce_view_invariants();
let view_borrow = self.view.borrow();
let view = view_borrow.deref();
unsafe {std::mem::transmute(view)}
unsafe { std::mem::transmute(view) }
}
fn view_mut(&mut self) -> &mut View {
@ -706,7 +706,8 @@ impl MutableRepo {
self.index.merge_in(other_repo.index());
self.enforce_view_invariants();
self.view.get_mut()
self.view
.get_mut()
.merge(self.index.as_index_ref(), &base_repo.view, &other_repo.view);
self.view_dirty = true;
}

View file

@ -20,7 +20,7 @@ use std::collections::{HashMap, HashSet, VecDeque};
use std::ffi::OsString;
use std::fmt::Debug;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::process::Command;
@ -223,6 +223,7 @@ struct WorkspaceCommandHelper {
workspace: Workspace,
repo: Arc<ReadonlyRepo>,
may_update_working_copy: bool,
working_copy_shared_with_git: bool,
working_copy_committed: bool,
// Whether to rebase descendants when the transaction finishes. This should generally be true
// for commands that rewrite commits.
@ -238,8 +239,11 @@ impl WorkspaceCommandHelper {
mut repo: Arc<ReadonlyRepo>,
) -> Result<Self, CommandError> {
let loaded_at_head = root_args.value_of("at_op").unwrap() == "@";
let mut working_copy_shared_with_git = false;
if let Some(git_repo) = repo.store().git_repo() {
if git_repo.workdir() == Some(workspace.workspace_root().as_path()) && loaded_at_head {
working_copy_shared_with_git =
git_repo.workdir() == Some(workspace.workspace_root().as_path());
if working_copy_shared_with_git && loaded_at_head {
let mut tx = repo.start_transaction("import git refs");
git::import_refs(tx.mut_repo(), &git_repo)?;
if tx.mut_repo().has_changes() {
@ -249,11 +253,15 @@ impl WorkspaceCommandHelper {
// Git HEAD.
let mut new_wc_commit = None;
if new_git_head != old_git_head && new_git_head.is_some() {
tx.mut_repo().record_abandoned_commit(repo.view().checkout().clone());
let new_checkout = repo.store().get_commit(new_git_head.as_ref().unwrap())?;
tx.mut_repo()
.record_abandoned_commit(repo.view().checkout().clone());
let new_checkout =
repo.store().get_commit(new_git_head.as_ref().unwrap())?;
let new_checkout = tx.mut_repo().check_out(ui.settings(), &new_checkout);
new_wc_commit = Some(new_checkout);
tx.mut_repo().create_descendant_rebaser(ui.settings()).rebase_all();
tx.mut_repo()
.create_descendant_rebaser(ui.settings())
.rebase_all();
}
repo = tx.commit();
if let Some(new_wc_commit) = new_wc_commit {
@ -269,6 +277,7 @@ impl WorkspaceCommandHelper {
workspace,
repo,
may_update_working_copy: loaded_at_head,
working_copy_shared_with_git,
working_copy_committed: false,
rebase_descendants: true,
})
@ -299,6 +308,10 @@ impl WorkspaceCommandHelper {
self.workspace.workspace_root()
}
fn working_copy_shared_with_git(&self) -> bool {
self.working_copy_shared_with_git
}
fn resolve_revision_arg(
&mut self,
ui: &mut Ui,
@ -1410,6 +1423,43 @@ fn short_commit_description(commit: &Commit) -> String {
format!("{} ({})", &commit.id().hex()[0..12], first_line)
}
fn add_to_git_exclude(ui: &mut Ui, git_repo: &git2::Repository) -> Result<(), CommandError> {
let exclude_file_path = git_repo.path().join("info").join("exclude");
if exclude_file_path.exists() {
match fs::OpenOptions::new()
.read(true)
.write(true)
.open(&exclude_file_path)
{
Ok(mut exclude_file) => {
let mut buf = vec![];
exclude_file.read_to_end(&mut buf)?;
let pattern = b"\n/.jj/\n";
if !buf.windows(pattern.len()).any(|window| window == pattern) {
exclude_file.seek(SeekFrom::End(0))?;
if !buf.ends_with(b"\n") {
exclude_file.write_all(b"\n")?;
}
exclude_file.write_all(b"/.jj/\n")?;
}
}
Err(err) => {
ui.write_error(&format!(
"Failed to add `.jj/` to {}: {}\n",
exclude_file_path.to_string_lossy(),
err
))?;
}
}
} else {
ui.write_error(&format!(
"Failed to add `.jj/` to {} because it doesn't exist\n",
exclude_file_path.to_string_lossy()
))?;
}
Ok(())
}
fn cmd_init(ui: &mut Ui, command: &CommandHelper, args: &ArgMatches) -> Result<(), CommandError> {
if args.is_present("git") && args.is_present("git-store") {
return Err(CommandError::UserError(String::from(
@ -1430,6 +1480,9 @@ fn cmd_init(ui: &mut Ui, command: &CommandHelper, args: &ArgMatches) -> Result<(
Workspace::init_external_git(ui.settings(), wc_path.clone(), git_store_path)?;
let git_repo = repo.store().git_repo().unwrap();
let mut workspace_command = command.for_loaded_repo(ui, workspace, repo)?;
if workspace_command.working_copy_shared_with_git() {
add_to_git_exclude(ui, &git_repo)?;
}
let mut tx = workspace_command.start_transaction("import git refs");
git::import_refs(tx.mut_repo(), &git_repo)?;
// TODO: Check out a recent commit. Maybe one with the highest generation