diff options
Diffstat (limited to 'asyncgit/src/sync/remotes/push.rs')
-rw-r--r-- | asyncgit/src/sync/remotes/push.rs | 904 |
1 files changed, 452 insertions, 452 deletions
diff --git a/asyncgit/src/sync/remotes/push.rs b/asyncgit/src/sync/remotes/push.rs index df0d7cfe..6cef918e 100644 --- a/asyncgit/src/sync/remotes/push.rs +++ b/asyncgit/src/sync/remotes/push.rs @@ -1,11 +1,11 @@ use super::utils; use crate::{ - error::{Error, Result}, - progress::ProgressPercent, - sync::{ - branch::branch_set_upstream, cred::BasicAuthCredential, - remotes::Callbacks, CommitId, - }, + error::{Error, Result}, + progress::ProgressPercent, + sync::{ + branch::branch_set_upstream, cred::BasicAuthCredential, + remotes::Callbacks, CommitId, + }, }; use crossbeam_channel::Sender; use git2::{PackBuilderStage, PushOptions}; @@ -13,467 +13,467 @@ use scopetime::scope_time; /// pub trait AsyncProgress: Clone + Send + Sync { - /// - fn is_done(&self) -> bool; - /// - fn progress(&self) -> ProgressPercent; + /// + fn is_done(&self) -> bool; + /// + fn progress(&self) -> ProgressPercent; } /// #[derive(Debug, Clone, PartialEq)] pub enum ProgressNotification { - /// - UpdateTips { - /// - name: String, - /// - a: CommitId, - /// - b: CommitId, - }, - /// - Transfer { - /// - objects: usize, - /// - total_objects: usize, - }, - /// - PushTransfer { - /// - current: usize, - /// - total: usize, - /// - bytes: usize, - }, - /// - Packing { - /// - stage: PackBuilderStage, - /// - total: usize, - /// - current: usize, - }, - /// - Done, + /// + UpdateTips { + /// + name: String, + /// + a: CommitId, + /// + b: CommitId, + }, + /// + Transfer { + /// + objects: usize, + /// + total_objects: usize, + }, + /// + PushTransfer { + /// + current: usize, + /// + total: usize, + /// + bytes: usize, + }, + /// + Packing { + /// + stage: PackBuilderStage, + /// + total: usize, + /// + current: usize, + }, + /// + Done, } impl AsyncProgress for ProgressNotification { - fn is_done(&self) -> bool { - *self == Self::Done - } - fn progress(&self) -> ProgressPercent { - match *self { - Self::Packing { - stage, - current, - total, - } => match stage { - PackBuilderStage::AddingObjects - | PackBuilderStage::Deltafication => { - ProgressPercent::new(current, total) - } - }, - Self::PushTransfer { current, total, .. } => { - ProgressPercent::new(current, total) - } - Self::Transfer { - objects, - total_objects, - .. - } => ProgressPercent::new(objects, total_objects), - _ => ProgressPercent::full(), - } - } + fn is_done(&self) -> bool { + *self == Self::Done + } + fn progress(&self) -> ProgressPercent { + match *self { + Self::Packing { + stage, + current, + total, + } => match stage { + PackBuilderStage::AddingObjects + | PackBuilderStage::Deltafication => { + ProgressPercent::new(current, total) + } + }, + Self::PushTransfer { current, total, .. } => { + ProgressPercent::new(current, total) + } + Self::Transfer { + objects, + total_objects, + .. + } => ProgressPercent::new(objects, total_objects), + _ => ProgressPercent::full(), + } + } } #[allow(clippy::redundant_pub_crate)] pub(crate) fn push( - repo_path: &str, - remote: &str, - branch: &str, - force: bool, - delete: bool, - basic_credential: Option<BasicAuthCredential>, - progress_sender: Option<Sender<ProgressNotification>>, + repo_path: &str, + remote: &str, + branch: &str, + force: bool, + delete: bool, + basic_credential: Option<BasicAuthCredential>, + progress_sender: Option<Sender<ProgressNotification>>, ) -> Result<()> { - scope_time!("push"); - - let repo = utils::repo(repo_path)?; - let mut remote = repo.find_remote(remote)?; - - let mut options = PushOptions::new(); - - let callbacks = Callbacks::new(progress_sender, basic_credential); - options.remote_callbacks(callbacks.callbacks()); - options.packbuilder_parallelism(0); - - let branch_modifier = match (force, delete) { - (true, true) => "+:", - (false, true) => ":", - (true, false) => "+", - (false, false) => "", - }; - let branch_name = - format!("{}refs/heads/{}", branch_modifier, branch); - remote.push(&[branch_name.as_str()], Some(&mut options))?; - - if let Some((reference, msg)) = - callbacks.get_stats()?.push_rejected_msg - { - return Err(Error::Generic(format!( - "push to '{}' rejected: {}", - reference, msg - ))); - } - - if !delete { - branch_set_upstream(&repo, branch)?; - } - - Ok(()) + scope_time!("push"); + + let repo = utils::repo(repo_path)?; + let mut remote = repo.find_remote(remote)?; + + let mut options = PushOptions::new(); + + let callbacks = Callbacks::new(progress_sender, basic_credential); + options.remote_callbacks(callbacks.callbacks()); + options.packbuilder_parallelism(0); + + let branch_modifier = match (force, delete) { + (true, true) => "+:", + (false, true) => ":", + (true, false) => "+", + (false, false) => "", + }; + let branch_name = + format!("{}refs/heads/{}", branch_modifier, branch); + remote.push(&[branch_name.as_str()], Some(&mut options))?; + + if let Some((reference, msg)) = + callbacks.get_stats()?.push_rejected_msg + { + return Err(Error::Generic(format!( + "push to '{}' rejected: {}", + reference, msg + ))); + } + + if !delete { + branch_set_upstream(&repo, branch)?; + } + + Ok(()) } #[cfg(test)] mod tests { - use super::*; - use crate::sync::{ - self, - tests::{ - get_commit_ids, repo_clone, repo_init, repo_init_bare, - write_commit_file, - }, - }; - use git2::Repository; - use std::{fs::File, io::Write, path::Path}; - - #[test] - fn test_force_push() { - // This test mimics the scenario of 2 people having 2 - // local branches and both modifying the same file then - // both pushing, sequentially - let (tmp_repo_dir, repo) = repo_init().unwrap(); - let (tmp_other_repo_dir, other_repo) = repo_init().unwrap(); - let (tmp_upstream_dir, _) = repo_init_bare().unwrap(); - - repo.remote( - "origin", - tmp_upstream_dir.path().to_str().unwrap(), - ) - .unwrap(); - - other_repo - .remote( - "origin", - tmp_upstream_dir.path().to_str().unwrap(), - ) - .unwrap(); - - let tmp_repo_file_path = - tmp_repo_dir.path().join("temp_file.txt"); - let mut tmp_repo_file = - File::create(tmp_repo_file_path).unwrap(); - writeln!(tmp_repo_file, "TempSomething").unwrap(); - - sync::commit( - tmp_repo_dir.path().to_str().unwrap(), - "repo_1_commit", - ) - .unwrap(); - - push( - tmp_repo_dir.path().to_str().unwrap(), - "origin", - "master", - false, - false, - None, - None, - ) - .unwrap(); - - let tmp_other_repo_file_path = - tmp_other_repo_dir.path().join("temp_file.txt"); - let mut tmp_other_repo_file = - File::create(tmp_other_repo_file_path).unwrap(); - writeln!(tmp_other_repo_file, "TempElse").unwrap(); - - sync::commit( - tmp_other_repo_dir.path().to_str().unwrap(), - "repo_2_commit", - ) - .unwrap(); - - // Attempt a normal push, - // should fail as branches diverged - assert_eq!( - push( - tmp_other_repo_dir.path().to_str().unwrap(), - "origin", - "master", - false, - false, - None, - None, - ) - .is_err(), - true - ); - - // Attempt force push, - // should work as it forces the push through - assert_eq!( - push( - tmp_other_repo_dir.path().to_str().unwrap(), - "origin", - "master", - true, - false, - None, - None, - ) - .is_err(), - false - ); - } - - #[test] - fn test_force_push_rewrites_history() { - // This test mimics the scenario of 2 people having 2 - // local branches and both modifying the same file then - // both pushing, sequentially - - let (tmp_repo_dir, repo) = repo_init().unwrap(); - let (tmp_other_repo_dir, other_repo) = repo_init().unwrap(); - let (tmp_upstream_dir, upstream) = repo_init_bare().unwrap(); - - repo.remote( - "origin", - tmp_upstream_dir.path().to_str().unwrap(), - ) - .unwrap(); - - other_repo - .remote( - "origin", - tmp_upstream_dir.path().to_str().unwrap(), - ) - .unwrap(); - - let tmp_repo_file_path = - tmp_repo_dir.path().join("temp_file.txt"); - let mut tmp_repo_file = - File::create(tmp_repo_file_path).unwrap(); - writeln!(tmp_repo_file, "TempSomething").unwrap(); - - sync::stage_add_file( - tmp_repo_dir.path().to_str().unwrap(), - Path::new("temp_file.txt"), - ) - .unwrap(); - - let repo_1_commit = sync::commit( - tmp_repo_dir.path().to_str().unwrap(), - "repo_1_commit", - ) - .unwrap(); - - //NOTE: make sure the commit actually contains that file - assert_eq!( - sync::get_commit_files( - tmp_repo_dir.path().to_str().unwrap(), - repo_1_commit - ) - .unwrap()[0] - .path, - String::from("temp_file.txt") - ); - - let commits = get_commit_ids(&repo, 1); - assert!(commits.contains(&repo_1_commit)); - - push( - tmp_repo_dir.path().to_str().unwrap(), - "origin", - "master", - false, - false, - None, - None, - ) - .unwrap(); - - let tmp_other_repo_file_path = - tmp_other_repo_dir.path().join("temp_file.txt"); - let mut tmp_other_repo_file = - File::create(tmp_other_repo_file_path).unwrap(); - writeln!(tmp_other_repo_file, "TempElse").unwrap(); - - sync::stage_add_file( - tmp_other_repo_dir.path().to_str().unwrap(), - Path::new("temp_file.txt"), - ) - .unwrap(); - - let repo_2_commit = sync::commit( - tmp_other_repo_dir.path().to_str().unwrap(), - "repo_2_commit", - ) - .unwrap(); - - let repo_2_parent = other_repo - .find_commit(repo_2_commit.into()) - .unwrap() - .parents() - .next() - .unwrap() - .id(); - - let commits = get_commit_ids(&other_repo, 1); - assert!(commits.contains(&repo_2_commit)); - - // Attempt a normal push, - // should fail as branches diverged - assert_eq!( - push( - tmp_other_repo_dir.path().to_str().unwrap(), - "origin", - "master", - false, - false, - None, - None, - ) - .is_err(), - true - ); - - // Check that the other commit is not in upstream, - // a normal push would not rewrite history - let commits = get_commit_ids(&upstream, 1); - assert!(!commits.contains(&repo_2_commit)); - - // Attempt force push, - // should work as it forces the push through - - push( - tmp_other_repo_dir.path().to_str().unwrap(), - "origin", - "master", - true, - false, - None, - None, - ) - .unwrap(); - - let commits = get_commit_ids(&upstream, 1); - assert!(commits.contains(&repo_2_commit)); - - let new_upstream_parent = - Repository::init_bare(tmp_upstream_dir.path()) - .unwrap() - .find_commit(repo_2_commit.into()) - .unwrap() - .parents() - .next() - .unwrap() - .id(); - assert_eq!(new_upstream_parent, repo_2_parent,); - } - - #[test] - fn test_delete_remote_branch() { - // This test mimics the scenario of a user creating a branch, push it, and then remove it on the remote - - let (upstream_dir, upstream_repo) = repo_init_bare().unwrap(); - - let (tmp_repo_dir, repo) = - repo_clone(upstream_dir.path().to_str().unwrap()) - .unwrap(); - - // You need a commit before being able to branch ! - let commit_1 = write_commit_file( - &repo, - "temp_file.txt", - "SomeContent", - "Initial commit", - ); - - let commits = get_commit_ids(&repo, 1); - assert!(commits.contains(&commit_1)); - - push( - tmp_repo_dir.path().to_str().unwrap(), - "origin", - "master", - false, - false, - None, - None, - ) - .unwrap(); - - // Create the local branch - sync::create_branch( - tmp_repo_dir.path().to_str().unwrap(), - "test_branch", - ) - .unwrap(); - - // Push the local branch - push( - tmp_repo_dir.path().to_str().unwrap(), - "origin", - "test_branch", - false, - false, - None, - None, - ) - .unwrap(); - - // Test if the branch exits on the remote - assert_eq!( - upstream_repo - .branches(None) - .unwrap() - .map(|i| i.unwrap()) - .map(|(i, _)| i.name().unwrap().unwrap().to_string()) - .filter(|i| i == "test_branch") - .next() - .is_some(), - true - ); - - // Delete the remote branch - assert_eq!( - push( - tmp_repo_dir.path().to_str().unwrap(), - "origin", - "test_branch", - false, - true, - None, - None, - ) - .is_ok(), - true - ); - - // Test that the branch has be remove from the remote - assert_eq!( - upstream_repo - .branches(None) - .unwrap() - .map(|i| i.unwrap()) - .map(|(i, _)| i.name().unwrap().unwrap().to_string()) - .filter(|i| i == "test_branch") - .next() - .is_some(), - false - ); - } + use super::*; + use crate::sync::{ + self, + tests::{ + get_commit_ids, repo_clone, repo_init, repo_init_bare, + write_commit_file, + }, + }; + use git2::Repository; + use std::{fs::File, io::Write, path::Path}; + + #[test] + fn test_force_push() { + // This test mimics the scenario of 2 people having 2 + // local branches and both modifying the same file then + // both pushing, sequentially + let (tmp_repo_dir, repo) = repo_init().unwrap(); + let (tmp_other_repo_dir, other_repo) = repo_init().unwrap(); + let (tmp_upstream_dir, _) = repo_init_bare().unwrap(); + + repo.remote( + "origin", + tmp_upstream_dir.path().to_str().unwrap(), + ) + .unwrap(); + + other_repo + .remote( + "origin", + tmp_upstream_dir.path().to_str().unwrap(), + ) + .unwrap(); + + let tmp_repo_file_path = + tmp_repo_dir.path().join("temp_file.txt"); + let mut tmp_repo_file = + File::create(tmp_repo_file_path).unwrap(); + writeln!(tmp_repo_file, "TempSomething").unwrap(); + + sync::commit( + tmp_repo_dir.path().to_str().unwrap(), + "repo_1_commit", + ) + .unwrap(); + + push( + tmp_repo_dir.path().to_str().unwrap(), + "origin", + "master", + false, + false, + None, + None, + ) + .unwrap(); + + let tmp_other_repo_file_path = + tmp_other_repo_dir.path().join("temp_file.txt"); + let mut tmp_other_repo_file = + File::create(tmp_other_repo_file_path).unwrap(); + writeln!(tmp_other_repo_file, "TempElse").unwrap(); + + sync::commit( + tmp_other_repo_dir.path().to_str().unwrap(), + "repo_2_commit", + ) + .unwrap(); + + // Attempt a normal push, + // should fail as branches diverged + assert_eq!( + push( + tmp_other_repo_dir.path().to_str().unwrap(), + "origin", + "master", + false, + false, + None, + None, + ) + .is_err(), + true + ); + + // Attempt force push, + // should work as it forces the push through + assert_eq!( + push( + tmp_other_repo_dir.path().to_str().unwrap(), + "origin", + "master", + true, + false, + None, + None, + ) + .is_err(), + false + ); + } + + #[test] + fn test_force_push_rewrites_history() { + // This test mimics the scenario of 2 people having 2 + // local branches and both modifying the same file then + // both pushing, sequentially + + let (tmp_repo_dir, repo) = repo_init().unwrap(); + let (tmp_other_repo_dir, other_repo) = repo_init().unwrap(); + let (tmp_upstream_dir, upstream) = repo_init_bare().unwrap(); + + repo.remote( + "origin", + tmp_upstream_dir.path().to_str().unwrap(), + ) + .unwrap(); + + other_repo + .remote( + "origin", + tmp_upstream_dir.path().to_str().unwrap(), + ) + .unwrap(); + + let tmp_repo_file_path = + tmp_repo_dir.path().join("temp_file.txt"); + let mut tmp_repo_file = + File::create(tmp_repo_file_path).unwrap(); + writeln!(tmp_repo_file, "TempSomething").unwrap(); + + sync::stage_add_file( + tmp_repo_dir.path().to_str().unwrap(), + Path::new("temp_file.txt"), + ) + .unwrap(); + + let repo_1_commit = sync::commit( + tmp_repo_dir.path().to_str().unwrap(), + "repo_1_commit", + ) + .unwrap(); + + //NOTE: make sure the commit actually contains that file + assert_eq!( + sync::get_commit_files( + tmp_repo_dir.path().to_str().unwrap(), + repo_1_commit + ) + .unwrap()[0] + .path, + String::from("temp_file.txt") + ); + + let commits = get_commit_ids(&repo, 1); + assert!(commits.contains(&repo_1_commit)); + + push( + tmp_repo_dir.path().to_str().unwrap(), + "origin", + "master", + false, + false, + None, + None, + ) + .unwrap(); + + let tmp_other_repo_file_path = + tmp_other_repo_dir.path().join("temp_file.txt"); + let mut tmp_other_repo_file = + File::create(tmp_other_repo_file_path).unwrap(); + writeln!(tmp_other_repo_file, "TempElse").unwrap(); + + sync::stage_add_file( + tmp_other_repo_dir.path().to_str().unwrap(), + Path::new("temp_file.txt"), + ) + .unwrap(); + + let repo_2_commit = sync::commit( + tmp_other_repo_dir.path().to_str().unwrap(), + "repo_2_commit", + ) + .unwrap(); + + let repo_2_parent = other_repo + .find_commit(repo_2_commit.into()) + .unwrap() + .parents() + .next() + .unwrap() + .id(); + + let commits = get_commit_ids(&other_repo, 1); + assert!(commits.contains(&repo_2_commit)); + + // Attempt a normal push, + // should fail as branches diverged + assert_eq!( + push( + tmp_other_repo_dir.path().to_str().unwrap(), + "origin", + "master", + false, + false, + None, + None, + ) + .is_err(), + true + ); + + // Check that the other commit is not in upstream, + // a normal push would not rewrite history + let commits = get_commit_ids(&upstream, 1); + assert!(!commits.contains(&repo_2_commit)); + + // Attempt force push, + // should work as it forces the push through + + push( + tmp_other_repo_dir.path().to_str().unwrap(), + "origin", + "master", + true, + false, + None, + None, + ) + .unwrap(); + + let commits = get_commit_ids(&upstream, 1); + assert!(commits.contains(&repo_2_commit)); + + let new_upstream_parent = + Repository::init_bare(tmp_upstream_dir.path()) + .unwrap() + .find_commit(repo_2_commit.into()) + .unwrap() + .parents() + .next() + .unwrap() + .id(); + assert_eq!(new_upstream_parent, repo_2_parent,); + } + + #[test] + fn test_delete_remote_branch() { + // This test mimics the scenario of a user creating a branch, push it, and then remove it on the remote + + let (upstream_dir, upstream_repo) = repo_init_bare().unwrap(); + + let (tmp_repo_dir, repo) = + repo_clone(upstream_dir.path().to_str().unwrap()) + .unwrap(); + + // You need a commit before being able to branch ! + let commit_1 = write_commit_file( + &repo, + "temp_file.txt", + "SomeContent", + "Initial commit", + ); + + let commits = get_commit_ids(&repo, 1); + assert!(commits.contains(&commit_1)); + + push( + tmp_repo_dir.path().to_str().unwrap(), + "origin", + "master", + false, + false, + None, + None, + ) + .unwrap(); + + // Create the local branch + sync::create_branch( + tmp_repo_dir.path().to_str().unwrap(), + "test_branch", + ) + .unwrap(); + + // Push the local branch + push( + tmp_repo_dir.path().to_str().unwrap(), + "origin", + "test_branch", + false, + false, + None, + None, + ) + .unwrap(); + + // Test if the branch exits on the remote + assert_eq!( + upstream_repo + .branches(None) + .unwrap() + .map(|i| i.unwrap()) + .map(|(i, _)| i.name().unwrap().unwrap().to_string()) + .filter(|i| i == "test_branch") + .next() + .is_some(), + true + ); + + // Delete the remote branch + assert_eq!( + push( + tmp_repo_dir.path().to_str().unwrap(), + "origin", + "test_branch", + false, + true, + None, + None, + ) + .is_ok(), + true + ); + + // Test that the branch has be remove from the remote + assert_eq!( + upstream_repo + .branches(None) + .unwrap() + .map(|i| i.unwrap()) + .map(|(i, _)| i.name().unwrap().unwrap().to_string()) + .filter(|i| i == "test_branch") + .next() + .is_some(), + false + ); + } } |