diff --git a/gitbutler-app/src/gb_repository/repository.rs b/gitbutler-app/src/gb_repository/repository.rs index 8305259c2..561f14018 100644 --- a/gitbutler-app/src/gb_repository/repository.rs +++ b/gitbutler-app/src/gb_repository/repository.rs @@ -265,7 +265,7 @@ impl Repository { .collect::>(); let src_target_reader = virtual_branches::target::Reader::new(&last_session_reader); - let dst_target_writer = virtual_branches::target::Writer::new(self, &self.project.path) + let dst_target_writer = virtual_branches::target::Writer::new(self, self.project.gb_dir()) .context("failed to open target writer for current session")?; // copy default target @@ -295,7 +295,7 @@ impl Repository { .with_context(|| format!("{}: failed to write target", branch.id))?; } - let dst_branch_writer = virtual_branches::branch::Writer::new(self, &self.project.path) + let dst_branch_writer = virtual_branches::branch::Writer::new(self, self.project.gb_dir()) .context("failed to open branch writer for current session")?; // copy branches that we don't already have diff --git a/gitbutler-app/src/projects/controller.rs b/gitbutler-app/src/projects/controller.rs index acfa4e22c..327508822 100644 --- a/gitbutler-app/src/projects/controller.rs +++ b/gitbutler-app/src/projects/controller.rs @@ -87,6 +87,11 @@ impl Controller { .add(&project) .context("failed to add project to storage")?; + // Create a .git/gitbutler directory for app data + if let Err(error) = std::fs::create_dir_all(project.gb_dir()) { + tracing::error!(project_id = %project.id, ?error, "failed to create {:?} on project add", project.gb_dir()); + } + if let Some(watchers) = &self.watchers { watchers.watch(&project)?; } @@ -155,10 +160,31 @@ impl Controller { } pub fn get(&self, id: &ProjectId) -> Result { - self.projects_storage.get(id).map_err(|error| match error { + let project = self.projects_storage.get(id).map_err(|error| match error { super::storage::Error::NotFound => GetError::NotFound, error => GetError::Other(error.into()), - }) + }); + if let Ok(project) = &project { + if !project.gb_dir().exists() { + if let Err(error) = std::fs::create_dir_all(project.gb_dir()) { + tracing::error!(project_id = %project.id, ?error, "failed to create {:?} on project get", project.gb_dir()); + } + } + // Clean up old virtual_branches.toml that was never used + if project + .path + .join(".git") + .join("virtual_branches.toml") + .exists() + { + if let Err(error) = + std::fs::remove_file(project.path.join(".git").join("virtual_branches.toml")) + { + tracing::error!(project_id = %project.id, ?error, "failed to remove old virtual_branches.toml"); + } + } + } + project } pub fn list(&self) -> Result, ListError> { diff --git a/gitbutler-app/src/projects/project.rs b/gitbutler-app/src/projects/project.rs index f04a5ae1d..ded7c8edb 100644 --- a/gitbutler-app/src/projects/project.rs +++ b/gitbutler-app/src/projects/project.rs @@ -1,4 +1,7 @@ -use std::{path, time}; +use std::{ + path::{self, PathBuf}, + time, +}; use serde::{Deserialize, Serialize}; @@ -99,4 +102,11 @@ impl Project { .map(|api| api.code_git_url.is_some()) .unwrap_or_default() } + + /// Returns the path to the directory containing the `GitButler` state for this project. + /// + /// Normally this is `.git/gitbutler` in the project's repository. + pub fn gb_dir(&self) -> PathBuf { + self.path.join(".git").join("gitbutler") + } } diff --git a/gitbutler-app/src/virtual_branches/base.rs b/gitbutler-app/src/virtual_branches/base.rs index 8d139de1c..26f0bdfa5 100644 --- a/gitbutler-app/src/virtual_branches/base.rs +++ b/gitbutler-app/src/virtual_branches/base.rs @@ -189,7 +189,7 @@ pub fn set_base_branch( sha: target_commit_oid, }; - let target_writer = target::Writer::new(gb_repository, &project_repository.project().path) + let target_writer = target::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create target writer")?; target_writer.write_default(&target)?; @@ -278,7 +278,7 @@ pub fn set_base_branch( }; let branch_writer = - branch::Writer::new(gb_repository, &project_repository.project().path) + branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create branch writer")?; branch_writer.write(&mut branch)?; } @@ -380,7 +380,7 @@ pub fn update_base_branch( target.sha ))?; - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create branch writer")?; let use_context = project_repository @@ -599,7 +599,7 @@ pub fn update_base_branch( )?; // write new target oid - let target_writer = target::Writer::new(gb_repository, &project_repository.project().path) + let target_writer = target::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create target writer")?; target_writer.write_default(&target::Target { sha: new_target_commit.id(), diff --git a/gitbutler-app/src/virtual_branches/branch/reader.rs b/gitbutler-app/src/virtual_branches/branch/reader.rs index 468307e7e..09918cf94 100644 --- a/gitbutler-app/src/virtual_branches/branch/reader.rs +++ b/gitbutler-app/src/virtual_branches/branch/reader.rs @@ -108,7 +108,7 @@ mod tests { let mut branch = test_branch(); - let writer = Writer::new(&gb_repository, &project.path)?; + let writer = Writer::new(&gb_repository, project.gb_dir())?; writer.write(&mut branch)?; let session = gb_repository.get_current_session()?.unwrap(); diff --git a/gitbutler-app/src/virtual_branches/branch/writer.rs b/gitbutler-app/src/virtual_branches/branch/writer.rs index 1c69cb0fe..3561c0577 100644 --- a/gitbutler-app/src/virtual_branches/branch/writer.rs +++ b/gitbutler-app/src/virtual_branches/branch/writer.rs @@ -20,7 +20,7 @@ impl<'writer> BranchWriter<'writer> { ) -> Result { let reader = reader::Reader::open(repository.root())?; let writer = writer::DirWriter::open(repository.root())?; - let state_handle = VirtualBranchesHandle::new(path.as_ref().join(".git").as_path()); + let state_handle = VirtualBranchesHandle::new(path.as_ref()); Ok(Self { repository, writer, @@ -232,7 +232,7 @@ mod tests { let mut branch = test_branch(); - let writer = BranchWriter::new(&gb_repository, &project.path)?; + let writer = BranchWriter::new(&gb_repository, project.gb_dir())?; writer.write(&mut branch)?; let root = gb_repository @@ -297,7 +297,7 @@ mod tests { let mut branch = test_branch(); - let writer = BranchWriter::new(&gb_repository, &project.path)?; + let writer = BranchWriter::new(&gb_repository, project.gb_dir())?; writer.write(&mut branch)?; assert!(gb_repository.get_current_session()?.is_some()); @@ -315,7 +315,7 @@ mod tests { let mut branch = test_branch(); - let writer = BranchWriter::new(&gb_repository, &project.path)?; + let writer = BranchWriter::new(&gb_repository, project.gb_dir())?; writer.write(&mut branch)?; let mut updated_branch = Branch { diff --git a/gitbutler-app/src/virtual_branches/integration.rs b/gitbutler-app/src/virtual_branches/integration.rs index 7b36afedf..a46478423 100644 --- a/gitbutler-app/src/virtual_branches/integration.rs +++ b/gitbutler-app/src/virtual_branches/integration.rs @@ -263,7 +263,7 @@ fn verify_head_is_clean( .context("failed to create virtual branch")?; // rebasing the extra commits onto the new branch - let writer = super::branch::Writer::new(gb_repository, &project_repository.project().path) + let writer = super::branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; extra_commits.reverse(); let mut head = new_branch.head; diff --git a/gitbutler-app/src/virtual_branches/iterator.rs b/gitbutler-app/src/virtual_branches/iterator.rs index 9cd1f3219..ceff3d7cc 100644 --- a/gitbutler-app/src/virtual_branches/iterator.rs +++ b/gitbutler-app/src/virtual_branches/iterator.rs @@ -152,10 +152,10 @@ mod tests { .. } = Suite::default().new_case(); - let target_writer = target::Writer::new(&gb_repository, &project.path)?; + let target_writer = target::Writer::new(&gb_repository, project.gb_dir())?; target_writer.write_default(&test_target())?; - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; let mut branch_1 = test_branch(); branch_writer.write(&mut branch_1)?; let mut branch_2 = test_branch(); diff --git a/gitbutler-app/src/virtual_branches/target/reader.rs b/gitbutler-app/src/virtual_branches/target/reader.rs index 4d02b7489..0bc3ef445 100644 --- a/gitbutler-app/src/virtual_branches/target/reader.rs +++ b/gitbutler-app/src/virtual_branches/target/reader.rs @@ -165,13 +165,13 @@ mod tests { sha: "0123456789abcdef0123456789abcdef01234567".parse().unwrap(), }; - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; branch_writer.write(&mut branch)?; let session = gb_repository.get_current_session()?.unwrap(); let session_reader = sessions::Reader::open(&gb_repository, &session)?; - let target_writer = TargetWriter::new(&gb_repository, &project.path)?; + let target_writer = TargetWriter::new(&gb_repository, project.gb_dir())?; let reader = TargetReader::new(&session_reader); target_writer.write_default(&default_target)?; diff --git a/gitbutler-app/src/virtual_branches/target/writer.rs b/gitbutler-app/src/virtual_branches/target/writer.rs index aed634236..a48276bab 100644 --- a/gitbutler-app/src/virtual_branches/target/writer.rs +++ b/gitbutler-app/src/virtual_branches/target/writer.rs @@ -24,7 +24,7 @@ impl<'writer> TargetWriter<'writer> { ) -> Result { let reader = reader::Reader::open(&repository.root())?; let writer = writer::DirWriter::open(repository.root())?; - let state_handle = VirtualBranchesHandle::new(path.as_ref().join(".git").as_path()); + let state_handle = VirtualBranchesHandle::new(path.as_ref()); Ok(Self { repository, writer, @@ -183,10 +183,10 @@ mod tests { sha: "0123456789abcdef0123456789abcdef01234567".parse().unwrap(), }; - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; branch_writer.write(&mut branch)?; - let target_writer = TargetWriter::new(&gb_repository, &project.path)?; + let target_writer = TargetWriter::new(&gb_repository, project.gb_dir())?; target_writer.write(&branch.id, &target)?; let root = gb_repository @@ -274,9 +274,9 @@ mod tests { sha: "0123456789abcdef0123456789abcdef01234567".parse().unwrap(), }; - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; branch_writer.write(&mut branch)?; - let target_writer = TargetWriter::new(&gb_repository, &project.path)?; + let target_writer = TargetWriter::new(&gb_repository, project.gb_dir())?; target_writer.write(&branch.id, &target)?; let updated_target = Target { diff --git a/gitbutler-app/src/virtual_branches/tests.rs b/gitbutler-app/src/virtual_branches/tests.rs index a4368012d..75a31ab9b 100644 --- a/gitbutler-app/src/virtual_branches/tests.rs +++ b/gitbutler-app/src/virtual_branches/tests.rs @@ -31,7 +31,7 @@ pub fn set_test_target( .expect("failed to add remote"); remote.push(&["refs/heads/master:refs/heads/master"], None)?; - target::Writer::new(gb_repo, &project_repository.project().path)? + target::Writer::new(gb_repo, project_repository.project().gb_dir())? .write_default(&target::Target { branch: "refs/remotes/origin/master".parse().unwrap(), remote_url: remote_repo.path().to_str().unwrap().parse().unwrap(), @@ -634,7 +634,7 @@ fn test_move_hunks_multiple_sources() -> Result<()> { let current_session = gb_repository.get_or_create_current_session()?; let current_session_reader = sessions::Reader::open(&gb_repository, ¤t_session)?; let branch_reader = branch::Reader::new(¤t_session_reader); - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; let mut branch2 = branch_reader.read(&branch2_id)?; branch2.ownership = BranchOwnershipClaims { claims: vec!["test.txt:1-5".parse()?], @@ -911,7 +911,7 @@ fn test_merge_vbranch_upstream_clean_rebase() -> Result<()> { )?; set_test_target(&gb_repository, &project_repository)?; - target::Writer::new(&gb_repository, &project_repository.project().path)?.write_default( + target::Writer::new(&gb_repository, project_repository.project().gb_dir())?.write_default( &target::Target { branch: "refs/remotes/origin/master".parse().unwrap(), remote_url: "origin".to_string(), @@ -927,7 +927,7 @@ fn test_merge_vbranch_upstream_clean_rebase() -> Result<()> { )?; let remote_branch: git::RemoteRefname = "refs/remotes/origin/master".parse().unwrap(); - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; let mut branch = create_virtual_branch( &gb_repository, &project_repository, @@ -1037,7 +1037,7 @@ fn test_merge_vbranch_upstream_conflict() -> Result<()> { )?; set_test_target(&gb_repository, &project_repository)?; - target::Writer::new(&gb_repository, &project.path)?.write_default(&target::Target { + target::Writer::new(&gb_repository, project.gb_dir())?.write_default(&target::Target { branch: "refs/remotes/origin/master".parse().unwrap(), remote_url: "origin".to_string(), sha: target_oid, @@ -1050,7 +1050,7 @@ fn test_merge_vbranch_upstream_conflict() -> Result<()> { )?; let remote_branch: git::RemoteRefname = "refs/remotes/origin/master".parse().unwrap(); - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; let mut branch = create_virtual_branch( &gb_repository, &project_repository, @@ -1412,7 +1412,7 @@ fn test_detect_mergeable_branch() -> Result<()> { let current_session = gb_repository.get_or_create_current_session()?; let current_session_reader = sessions::Reader::open(&gb_repository, ¤t_session)?; let branch_reader = branch::Reader::new(¤t_session_reader); - let branch_writer = branch::Writer::new(&gb_repository, &project.path)?; + let branch_writer = branch::Writer::new(&gb_repository, project.gb_dir())?; update_branch( &gb_repository, @@ -1606,7 +1606,7 @@ fn test_upstream_integrated_vbranch() -> Result<()> { "update target", )?; - target::Writer::new(&gb_repository, &project_repository.project().path)?.write_default( + target::Writer::new(&gb_repository, project_repository.project().gb_dir())?.write_default( &target::Target { branch: "refs/remotes/origin/master".parse().unwrap(), remote_url: "http://origin.com/project".to_string(), diff --git a/gitbutler-app/src/virtual_branches/virtual.rs b/gitbutler-app/src/virtual_branches/virtual.rs index 37810635f..d7e95919a 100644 --- a/gitbutler-app/src/virtual_branches/virtual.rs +++ b/gitbutler-app/src/virtual_branches/virtual.rs @@ -219,7 +219,7 @@ pub fn apply_branch( }) })?; - let writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create branch writer")?; let mut branch = match branch::Reader::new(¤t_session_reader).read(branch_id) { @@ -662,7 +662,7 @@ pub fn unapply_branch( .find_commit(default_target.sha) .context("failed to find target commit")?; - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; let final_tree = if conflicts::is_resolving(project_repository) { @@ -1286,7 +1286,7 @@ pub fn create_virtual_branch( .unwrap_or(all_virtual_branches.len()) .clamp(0, all_virtual_branches.len()); - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; let selected_for_changes = if let Some(selected_for_changes) = create.selected_for_changes { @@ -1505,8 +1505,9 @@ pub fn merge_virtual_branch_upstream( let merge_tree = repo .find_tree(merge_tree_oid) .context("failed to find merge tree")?; - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) - .context("failed to create writer")?; + let branch_writer = + branch::Writer::new(gb_repository, project_repository.project().gb_dir()) + .context("failed to create writer")?; if *project_repository.project().ok_with_force_push { // attempt a rebase @@ -1614,7 +1615,7 @@ pub fn update_branch( let current_session_reader = sessions::Reader::open(gb_repository, ¤t_session) .context("failed to open current session")?; let branch_reader = branch::Reader::new(¤t_session_reader); - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; let mut branch = branch_reader @@ -1720,7 +1721,7 @@ pub fn delete_branch( let current_session_reader = sessions::Reader::open(gb_repository, ¤t_session) .context("failed to open current session")?; let branch_reader = branch::Reader::new(¤t_session_reader); - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; let branch = match branch_reader.read(branch_id) { @@ -2151,8 +2152,9 @@ fn get_applied_status( // write updated state if not resolving if !project_repository.is_resolving() { - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) - .context("failed to create writer")?; + let branch_writer = + branch::Writer::new(gb_repository, project_repository.project().gb_dir()) + .context("failed to create writer")?; for (vbranch, files) in &mut hunks_by_branch { vbranch.tree = write_tree(project_repository, default_target, files)?; branch_writer @@ -2238,7 +2240,7 @@ pub fn reset_branch( )); } - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; branch.head = target_commit_oid; branch_writer @@ -2576,7 +2578,7 @@ pub fn commit( } // update the virtual branch head - let writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; branch.tree = tree_oid; branch.head = commit_oid; @@ -2605,7 +2607,7 @@ pub fn push( .map_err(errors::PushError::Other)?; let branch_reader = branch::Reader::new(¤t_session_reader); - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; let mut vbranch = branch_reader.read(branch_id).map_err(|error| match error { @@ -3049,7 +3051,7 @@ pub fn amend( ) .context("failed to create commit")?; - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; target_branch.head = commit_oid; branch_writer.write(target_branch)?; @@ -3232,7 +3234,7 @@ pub fn cherry_pick( .context("failed to checkout final tree")?; // update branch status - let writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; branch.head = commit_oid; writer @@ -3425,7 +3427,7 @@ pub fn squash( }; // save new branch head - let writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; branch.head = new_head_id; writer @@ -3602,7 +3604,7 @@ pub fn update_commit_message( }; // save new branch head - let writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; branch.head = new_head_id; writer @@ -3728,7 +3730,7 @@ pub fn move_commit( return Err(errors::MoveCommitError::SourceLocked); } - let branch_writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let branch_writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; let branch_reader = branch::Reader::new(&latest_session_reader); @@ -3946,7 +3948,7 @@ pub fn create_virtual_branch_from_branch( selected_for_changes, }; - let writer = branch::Writer::new(gb_repository, &project_repository.project().path) + let writer = branch::Writer::new(gb_repository, project_repository.project().gb_dir()) .context("failed to create writer")?; writer .write(&mut branch) diff --git a/gitbutler-app/src/watcher/handlers/calculate_deltas_handler.rs b/gitbutler-app/src/watcher/handlers/calculate_deltas_handler.rs index 0977bf097..d20f46c86 100644 --- a/gitbutler-app/src/watcher/handlers/calculate_deltas_handler.rs +++ b/gitbutler-app/src/watcher/handlers/calculate_deltas_handler.rs @@ -828,8 +828,10 @@ mod test { )])); let listener = Handler::from_path(&suite.local_app_data); - let branch_writer = virtual_branches::branch::Writer::new(&gb_repository, &project.path)?; - let target_writer = virtual_branches::target::Writer::new(&gb_repository, &project.path)?; + let branch_writer = + virtual_branches::branch::Writer::new(&gb_repository, project.gb_dir())?; + let target_writer = + virtual_branches::target::Writer::new(&gb_repository, project.gb_dir())?; let default_target = test_target(); target_writer.write_default(&default_target)?; let mut vbranch0 = test_branch(); @@ -884,8 +886,10 @@ mod test { )])); let listener = Handler::from_path(&suite.local_app_data); - let branch_writer = virtual_branches::branch::Writer::new(&gb_repository, &project.path)?; - let target_writer = virtual_branches::target::Writer::new(&gb_repository, &project.path)?; + let branch_writer = + virtual_branches::branch::Writer::new(&gb_repository, project.gb_dir())?; + let target_writer = + virtual_branches::target::Writer::new(&gb_repository, project.gb_dir())?; let default_target = test_target(); target_writer.write_default(&default_target)?; let mut vbranch0 = test_branch();