From f7916c790758e5b304a7c97c48a031fc25032fb1 Mon Sep 17 00:00:00 2001 From: Nikita Galaiko Date: Wed, 11 Oct 2023 14:33:01 +0200 Subject: [PATCH] add requires_force calculation for vbranch --- packages/tauri/src/git/oid.rs | 11 +- packages/tauri/src/keys/key.rs | 6 ++ .../src/project_repository/repository.rs | 30 ++++-- packages/tauri/src/virtual_branches/tests.rs | 101 +++++++++++++----- .../tauri/src/virtual_branches/virtual.rs | 18 +++- 5 files changed, 124 insertions(+), 42 deletions(-) diff --git a/packages/tauri/src/git/oid.rs b/packages/tauri/src/git/oid.rs index da3f35b71..f5ad7e6a6 100644 --- a/packages/tauri/src/git/oid.rs +++ b/packages/tauri/src/git/oid.rs @@ -1,12 +1,21 @@ use std::{fmt, str::FromStr}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Copy, Clone, Hash, Eq)] pub struct Oid { oid: git2::Oid, } +impl Serialize for Oid { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.oid.to_string().serialize(serializer) + } +} + impl<'de> Deserialize<'de> for Oid { fn deserialize(deserializer: D) -> Result where diff --git a/packages/tauri/src/keys/key.rs b/packages/tauri/src/keys/key.rs index 1286bbd1c..37d96013b 100644 --- a/packages/tauri/src/keys/key.rs +++ b/packages/tauri/src/keys/key.rs @@ -14,6 +14,12 @@ pub enum Key { }, } +impl From for Key { + fn from(value: PrivateKey) -> Self { + Self::Generated(Box::new(value)) + } +} + #[derive(Debug)] pub struct PrivateKey(ssh_key::PrivateKey); diff --git a/packages/tauri/src/project_repository/repository.rs b/packages/tauri/src/project_repository/repository.rs index 79c683369..00a423a55 100644 --- a/packages/tauri/src/project_repository/repository.rs +++ b/packages/tauri/src/project_repository/repository.rs @@ -206,14 +206,26 @@ impl Repository { .context("failed to find remote") .map_err(RemoteError::Other)?; - if let Ok(Some(url)) = remote.url() { - match url.as_ssh() { - Ok(ssh_url) => Ok(self - .git_repository - .remote_anonymous(&ssh_url) - .context("failed to get anonymous") - .map_err(RemoteError::Other)?), - Err(_) => Err(RemoteError::NonSSHUrl(url.to_string())), + let remote_url = remote + .url() + .context("failed to get remote url") + .map_err(RemoteError::Other)?; + + if let Some(url) = remote_url { + match url.scheme { + #[cfg(test)] + git::Scheme::File => Ok(remote), + git::Scheme::Ssh => Ok(remote), + _ => { + let ssh_url = url + .as_ssh() + .map_err(|_| RemoteError::NonSSHUrl(url.to_string()))?; + Ok(self + .git_repository + .remote_anonymous(&ssh_url) + .context("failed to get anonymous") + .map_err(RemoteError::Other)?) + } } } else { Err(RemoteError::NoUrl) @@ -373,7 +385,7 @@ pub enum RemoteError { #[error("authentication failed")] AuthError, #[error(transparent)] - Other(anyhow::Error), + Other(#[from] anyhow::Error), } impl From for crate::error::Error { diff --git a/packages/tauri/src/virtual_branches/tests.rs b/packages/tauri/src/virtual_branches/tests.rs index bf6de2e47..7a65989c9 100644 --- a/packages/tauri/src/virtual_branches/tests.rs +++ b/packages/tauri/src/virtual_branches/tests.rs @@ -159,9 +159,7 @@ fn test_signed_commit() -> Result<()> { let branches = list_virtual_branches(&gb_repository, &project_repository).unwrap(); let commit_id = &branches[0].commits[0].id; - let commit_obj = project_repository - .git_repository - .find_commit(commit_id.parse().unwrap())?; + let commit_obj = project_repository.git_repository.find_commit(*commit_id)?; // check the raw_header contains the string "SSH SIGNATURE" assert!(commit_obj.raw_header().unwrap().contains("SSH SIGNATURE")); @@ -248,9 +246,7 @@ fn test_track_binary_files() -> Result<()> { // status (no files) let branches = list_virtual_branches(&gb_repository, &project_repository).unwrap(); let commit_id = &branches[0].commits[0].id; - let commit_obj = project_repository - .git_repository - .find_commit(commit_id.parse().unwrap())?; + let commit_obj = project_repository.git_repository.find_commit(*commit_id)?; let tree = commit_obj.tree()?; let files = tree_to_entry_list(&project_repository.git_repository, &tree); assert_eq!(files[0].0, "image.bin"); @@ -279,9 +275,7 @@ fn test_track_binary_files() -> Result<()> { let branches = list_virtual_branches(&gb_repository, &project_repository).unwrap(); let commit_id = &branches[0].commits[0].id; // get tree from commit_id - let commit_obj = project_repository - .git_repository - .find_commit(commit_id.parse().unwrap())?; + let commit_obj = project_repository.git_repository.find_commit(*commit_id)?; let tree = commit_obj.tree()?; let files = tree_to_entry_list(&project_repository.git_repository, &tree); @@ -1033,7 +1027,7 @@ fn test_update_base_branch_base() -> Result<()> { let branch = &branches[0]; assert_eq!(branch.files.len(), 1); assert_eq!(branch.commits.len(), 1); // branch commit, rebased - let head_sha = branch.commits[0].id.parse::()?; + let head_sha = branch.commits[0].id; let head_commit = project_repository.git_repository.find_commit(head_sha)?; let parent = head_commit.parent(0)?; @@ -1403,9 +1397,7 @@ fn test_merge_vbranch_upstream_clean() -> Result<()> { // make sure the last commit was signed let last_id = &branch1.commits[0].id; - let last_commit = project_repository - .git_repository - .find_commit(last_id.parse::()?)?; + let last_commit = project_repository.git_repository.find_commit(*last_id)?; assert!(last_commit.raw_header().unwrap().contains("SSH SIGNATURE")); Ok(()) @@ -1550,9 +1542,7 @@ fn test_merge_vbranch_upstream_conflict() -> Result<()> { // make sure the last commit was a merge commit (2 parents) let last_id = &branch1.commits[0].id; - let last_commit = project_repository - .git_repository - .find_commit(last_id.parse::()?)?; + let last_commit = project_repository.git_repository.find_commit(*last_id)?; assert_eq!(last_commit.parent_count(), 2); Ok(()) @@ -3192,12 +3182,9 @@ fn test_commit_partial_by_file() -> Result<()> { // branch one test.txt has just the 1st and 3rd hunks applied let commit2 = &branch1.commits[0].id; - let commit2 = commit2 - .parse::() - .expect("failed to parse commit id"); let commit2 = project_repository .git_repository - .find_commit(commit2) + .find_commit(*commit2) .expect("failed to get commit object"); let tree = commit1.tree().expect("failed to get tree"); @@ -3266,12 +3253,9 @@ fn test_commit_add_and_delete_files() -> Result<()> { // branch one test.txt has just the 1st and 3rd hunks applied let commit2 = &branch1.commits[0].id; - let commit2 = commit2 - .parse::() - .expect("failed to parse commit id"); let commit2 = project_repository .git_repository - .find_commit(commit2) + .find_commit(*commit2) .expect("failed to get commit object"); let tree = commit1.tree().expect("failed to get tree"); @@ -3333,12 +3317,9 @@ fn test_commit_executable_and_symlinks() -> Result<()> { let branch1 = &branches.iter().find(|b| b.id == branch1_id).unwrap(); let commit = &branch1.commits[0].id; - let commit = commit - .parse::() - .expect("failed to parse commit id"); let commit = project_repository .git_repository - .find_commit(commit) + .find_commit(*commit) .expect("failed to get commit object"); let tree = commit.tree().expect("failed to get tree"); @@ -3778,7 +3759,7 @@ fn test_apply_out_of_date_conflicting_vbranch() -> Result<()> { let branches = list_virtual_branches(&gb_repository, &project_repository)?; let branch1 = &branches.iter().find(|b| &b.id == branch_id).unwrap(); let last_commit = branch1.commits.first().unwrap(); - let last_commit_oid = last_commit.id.parse::()?; + let last_commit_oid = last_commit.id; let commit = gb_repository.git_repository.find_commit(last_commit_oid)?; assert!(!branch1.conflicted); assert_eq!(commit.parent_count(), 2); @@ -4107,3 +4088,65 @@ fn test_reset_to_target() { "2" ); } + +#[test] +fn test_requires_force() { + let suite = Suite::default(); + let Case { + project_repository, + gb_repository, + project, + .. + } = suite.new_case(); + set_test_target(&gb_repository, &project_repository).unwrap(); + + let branch_id = create_virtual_branch(&gb_repository, &BranchCreateRequest::default()) + .expect("failed to create virtual branch") + .id; + + fs::write(project.path.join("file.txt"), "1").unwrap(); + commit( + &gb_repository, + &project_repository, + &branch_id, + "commit 1", + None, + None, + None, + ) + .unwrap(); + + fs::write(project.path.join("file.txt"), "2").unwrap(); + commit( + &gb_repository, + &project_repository, + &branch_id, + "commit 2", + None, + None, + None, + ) + .unwrap(); + + push( + &project_repository, + &gb_repository, + &branch_id, + &keys::Key::from(suite.keys.get_or_create().unwrap()), + ) + .unwrap(); + + let statuses = list_virtual_branches(&gb_repository, &project_repository).unwrap(); + assert!(!statuses[0].requires_force); + + reset_branch( + &gb_repository, + &project_repository, + &branch_id, + statuses[0].commits[1].id, + ) + .unwrap(); + + let statuses = list_virtual_branches(&gb_repository, &project_repository).unwrap(); + assert!(statuses[0].requires_force); +} diff --git a/packages/tauri/src/virtual_branches/virtual.rs b/packages/tauri/src/virtual_branches/virtual.rs index 3cdac8b78..737aa3f43 100644 --- a/packages/tauri/src/virtual_branches/virtual.rs +++ b/packages/tauri/src/virtual_branches/virtual.rs @@ -33,6 +33,7 @@ use super::{ // #[derive(Debug, PartialEq, Clone, Serialize)] #[serde(rename_all = "camelCase")] +#[allow(clippy::struct_excessive_bools)] pub struct VirtualBranch { pub id: String, pub name: String, @@ -40,6 +41,7 @@ pub struct VirtualBranch { pub active: bool, pub files: Vec, pub commits: Vec, + pub requires_force: bool, // does this branch require a force push to the upstream? pub conflicted: bool, // is this branch currently in a conflicted state (only for applied branches) pub order: usize, // the order in which this branch should be displayed in the UI pub upstream: Option, // the name of the upstream branch this branch this pushes to @@ -59,7 +61,7 @@ pub struct VirtualBranch { #[derive(Debug, PartialEq, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct VirtualBranchCommit { - pub id: String, + pub id: git::Oid, pub description: String, pub created_at: u128, pub author: Author, @@ -637,6 +639,16 @@ pub fn list_virtual_branches( } } + let requires_force = if let Some(upstream) = &branch.upstream { + let upstream_commit = repo + .find_commit(repo.refname_to_id(&upstream.to_string())?) + .context("failed to find upstream commit")?; + let merge_base = repo.merge_base(upstream_commit.id(), branch.head)?; + merge_base != upstream_commit.id() + } else { + false + }; + let branch = VirtualBranch { id: branch.id.to_string(), name: branch.name.to_string(), @@ -645,6 +657,7 @@ pub fn list_virtual_branches( files: vfiles, order: branch.order, commits, + requires_force, upstream: branch.upstream.clone(), conflicted: conflicts::is_resolving(project_repository), base_current, @@ -810,7 +823,6 @@ pub fn commit_to_vbranch_commit( let timestamp = commit.time().seconds() as u128; let signature = commit.author(); let message = commit.message().unwrap().to_string(); - let sha = commit.id().to_string(); let is_remote = match upstream_commits { Some(commits) => commits.contains_key(&commit.id()), @@ -823,7 +835,7 @@ pub fn commit_to_vbranch_commit( let is_integrated = is_commit_integrated(repository, target, commit)?; let commit = VirtualBranchCommit { - id: sha, + id: commit.id(), created_at: timestamp * 1000, author: Author::from(signature), description: message,