diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 1036b5ec95..d30f32dd84 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -11,8 +11,8 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1607fbce55..76178a8b83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,6 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] rust: [nightly, stable] - runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.rust == 'nightly' }} @@ -54,14 +53,13 @@ jobs: build-linux-musl: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@master - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal target: x86_64-unknown-linux-musl - - name: Setup MUSL run: | sudo apt-get -qq install musl-tools @@ -73,6 +71,9 @@ jobs: run: | make build-linux-musl-release ./target/x86_64-unknown-linux-musl/release/gitui --version + - name: Test + run: | + make test-linux-musl rustfmt: name: Rustfmt diff --git a/Cargo.lock b/Cargo.lock index ef27c69104..a468f93c1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -414,6 +414,8 @@ dependencies = [ "libc", "libgit2-sys", "log", + "openssl-probe", + "openssl-sys", "url", ] @@ -552,10 +554,26 @@ checksum = "0100ae90655025134424939f1f60e27e879460d451dff6afedde4f8226cbebfc" dependencies = [ "cc", "libc", + "libssh2-sys", "libz-sys", + "openssl-sys", "pkg-config", ] +[[package]] +name = "libssh2-sys" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca46220853ba1c512fc82826d0834d87b06bcd3c2a42241b7de72f3d2fe17056" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.0" @@ -785,6 +803,35 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-src" +version = "111.10.2+1.1.1g" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a287fdb22e32b5b60624d4a5a7a02dbe82777f730ec0dbc42a0554326fef5a70" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +dependencies = [ + "autocfg", + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.10.2" diff --git a/Makefile b/Makefile index fa537a24bf..ac0688366c 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,9 @@ build-linux-musl-debug: build-linux-musl-release: cargo build --release --target=x86_64-unknown-linux-musl --no-default-features +test-linux-musl: + cargo test --workspace --target=x86_64-unknown-linux-musl --no-default-features + test: cargo test --workspace diff --git a/assets/vim_style_key_config.ron b/assets/vim_style_key_config.ron index 129c76e630..6e4735b23d 100644 --- a/assets/vim_style_key_config.ron +++ b/assets/vim_style_key_config.ron @@ -59,4 +59,5 @@ commit_amend: ( code: Char('A'), modifiers: ( bits: 0,),), copy: ( code: Char('y'), modifiers: ( bits: 0,),), create_branch: ( code: Char('b'), modifiers: ( bits: 0,),), + push: ( code: Char('p'), modifiers: ( bits: 0,),), ) diff --git a/asyncgit/Cargo.toml b/asyncgit/Cargo.toml index e0c22963b6..621f07ebcb 100644 --- a/asyncgit/Cargo.toml +++ b/asyncgit/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["git"] [dependencies] scopetime = { path = "../scopetime", version = "0.1" } -git2 = { version = "0.13.10", default-features = false } +git2 = { version = "0.13", features = ["vendored-openssl"] } rayon-core = "1.8" crossbeam-channel = "0.4" log = "0.4" diff --git a/asyncgit/src/cached/branchname.rs b/asyncgit/src/cached/branchname.rs index e5df31aa7b..8464458aa7 100644 --- a/asyncgit/src/cached/branchname.rs +++ b/asyncgit/src/cached/branchname.rs @@ -32,6 +32,11 @@ impl BranchName { self.fetch(current_head) } + /// + pub fn last(&self) -> Option<String> { + self.last_result.as_ref().map(|last| last.1.clone()) + } + fn fetch(&mut self, head: Head) -> Result<String> { let name = sync::get_branch_name(self.repo_path.as_str())?; self.last_result = Some((head, name.clone())); diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index 58b32148be..4eb97b6967 100644 --- a/asyncgit/src/sync/mod.rs +++ b/asyncgit/src/sync/mod.rs @@ -10,6 +10,7 @@ mod hooks; mod hunks; mod ignore; mod logwalker; +mod remotes; mod reset; mod stash; pub mod status; @@ -29,6 +30,9 @@ pub use hooks::{hooks_commit_msg, hooks_post_commit, HookResult}; pub use hunks::{reset_hunk, stage_hunk, unstage_hunk}; pub use ignore::add_to_ignore; pub use logwalker::LogWalker; +pub use remotes::{ + fetch_origin, get_remotes, push_origin, remote_push_master, +}; pub use reset::{reset_stage, reset_workdir}; pub use stash::{get_stashes, stash_apply, stash_drop, stash_save}; pub use tags::{get_tags, CommitTags, Tags}; diff --git a/asyncgit/src/sync/remotes.rs b/asyncgit/src/sync/remotes.rs new file mode 100644 index 0000000000..916b4f876d --- /dev/null +++ b/asyncgit/src/sync/remotes.rs @@ -0,0 +1,103 @@ +//! + +use crate::{error::Result, sync::utils}; +use git2::{Cred, FetchOptions, PushOptions, RemoteCallbacks}; +use scopetime::scope_time; + +/// +pub fn get_remotes(repo_path: &str) -> Result<Vec<String>> { + scope_time!("get_remotes"); + + let repo = utils::repo(repo_path)?; + let remotes = repo.remotes()?; + let remotes: Vec<String> = + remotes.iter().filter_map(|s| s).map(String::from).collect(); + + Ok(remotes) +} + +/// +pub fn remote_push_master(repo_path: &str) -> Result<()> { + scope_time!("remote_push_master"); + + let repo = utils::repo(repo_path)?; + let mut remote = repo.find_remote("origin")?; + + remote.push(&["refs/heads/master"], None)?; + + Ok(()) +} + +/// +pub fn fetch_origin(repo_path: &str, branch: &str) -> Result<usize> { + scope_time!("remote_fetch_master"); + + let repo = utils::repo(repo_path)?; + let mut remote = repo.find_remote("origin")?; + + let mut options = FetchOptions::new(); + options.remote_callbacks(remote_callbacks()); + + remote.fetch(&[branch], Some(&mut options), None)?; + + Ok(remote.stats().received_bytes()) +} + +/// +pub fn push_origin(repo_path: &str, branch: &str) -> Result<()> { + scope_time!("push_origin"); + + let repo = utils::repo(repo_path)?; + let mut remote = repo.find_remote("origin")?; + + let mut options = PushOptions::new(); + options.remote_callbacks(remote_callbacks()); + + remote.push(&[branch], Some(&mut options))?; + + Ok(()) +} + +fn remote_callbacks<'a>() -> RemoteCallbacks<'a> { + let mut callbacks = RemoteCallbacks::new(); + callbacks.credentials(|url, username_from_url, allowed_types| { + log::debug!( + "creds: '{}' {:?} ({:?})", + url, + username_from_url, + allowed_types + ); + + Cred::ssh_key_from_agent( + username_from_url.expect("username not found"), + ) + }); + + callbacks +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::sync::tests::debug_cmd_print; + use tempfile::TempDir; + + #[test] + fn test_smoke() { + let td = TempDir::new().unwrap(); + + debug_cmd_print( + td.path().as_os_str().to_str().unwrap(), + "git clone https://github.com/extrawurst/brewdump.git", + ); + + let repo_path = td.path().join("brewdump"); + let repo_path = repo_path.as_os_str().to_str().unwrap(); + + let remotes = get_remotes(repo_path).unwrap(); + + assert_eq!(remotes, vec![String::from("origin")]); + + fetch_origin(repo_path, "master").unwrap(); + } +} diff --git a/src/app.rs b/src/app.rs index eb7958ff5a..adb6f1e411 100644 --- a/src/app.rs +++ b/src/app.rs @@ -256,7 +256,7 @@ impl App { let msg = format!("failed to launch editor:\n{}", e); log::error!("{}", msg.as_str()); - self.msg.show_msg(msg.as_str())?; + self.msg.show_error(msg.as_str())?; } self.requires_redraw.set(true); @@ -454,7 +454,12 @@ impl App { flags.insert(NeedsUpdate::COMMANDS); } InternalEvent::ShowErrorMsg(msg) => { - self.msg.show_msg(msg.as_str())?; + self.msg.show_error(msg.as_str())?; + flags + .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); + } + InternalEvent::ShowInfoMsg(msg) => { + self.msg.show_info(msg.as_str())?; flags .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); } diff --git a/src/clipboard.rs b/src/clipboard.rs index b0c3e42ff4..2828ff6577 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -20,7 +20,7 @@ pub fn copy_string(_string: String) -> Result<()> { } #[cfg(feature = "clipboard")] -pub fn is_supported() -> bool { +pub const fn is_supported() -> bool { true } diff --git a/src/components/changes.rs b/src/components/changes.rs index 396afda99e..acdf5e3604 100644 --- a/src/components/changes.rs +++ b/src/components/changes.rs @@ -64,6 +64,11 @@ impl ChangesComponent { Ok(()) } + /// + pub fn branch_name(&self) -> Option<String> { + self.branch_name.last() + } + /// pub fn set_items(&mut self, list: &[StatusItem]) -> Result<()> { self.files.update(list)?; diff --git a/src/components/msg.rs b/src/components/msg.rs index 6320b3f610..d3c6e87bc0 100644 --- a/src/components/msg.rs +++ b/src/components/msg.rs @@ -14,6 +14,7 @@ use tui::{ use ui::style::SharedTheme; pub struct MsgComponent { + title: String, msg: String, visible: bool, theme: SharedTheme, @@ -39,9 +40,7 @@ impl DrawableComponent for MsgComponent { Paragraph::new(txt.iter()) .block( Block::default() - .title(&strings::msg_title_error( - &self.key_config, - )) + .title(self.title.as_str()) .title_style(self.theme.text_danger()) .borders(Borders::ALL) .border_type(BorderType::Thick), @@ -104,14 +103,26 @@ impl MsgComponent { key_config: SharedKeyConfig, ) -> Self { Self { + title: String::new(), msg: String::new(), visible: false, theme, key_config, } } + + /// + pub fn show_error(&mut self, msg: &str) -> Result<()> { + self.title = strings::msg_title_error(&self.key_config); + self.msg = msg.to_string(); + self.show()?; + + Ok(()) + } + /// - pub fn show_msg(&mut self, msg: &str) -> Result<()> { + pub fn show_info(&mut self, msg: &str) -> Result<()> { + self.title = strings::msg_title_info(&self.key_config); self.msg = msg.to_string(); self.show()?; diff --git a/src/keys.rs b/src/keys.rs index 41c1e7ffd5..24d0ea1ee9 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -60,6 +60,7 @@ pub struct KeyConfig { pub commit_amend: KeyEvent, pub copy: KeyEvent, pub create_branch: KeyEvent, + pub push: KeyEvent, } #[rustfmt::skip] @@ -109,6 +110,7 @@ impl Default for KeyConfig { commit_amend: KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::CONTROL}, copy: KeyEvent { code: KeyCode::Char('y'), modifiers: KeyModifiers::empty()}, create_branch: KeyEvent { code: KeyCode::Char('b'), modifiers: KeyModifiers::empty()}, + push: KeyEvent { code: KeyCode::Char('p'), modifiers: KeyModifiers::empty()}, } } } diff --git a/src/queue.rs b/src/queue.rs index 3e3478f3ff..982584890c 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -39,6 +39,8 @@ pub enum InternalEvent { /// ShowErrorMsg(String), /// + ShowInfoMsg(String), + /// Update(NeedsUpdate), /// open commit msg input OpenCommit, diff --git a/src/strings.rs b/src/strings.rs index 077708777d..4f8da04734 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -40,6 +40,9 @@ pub fn msg_opening_editor(_key_config: &SharedKeyConfig) -> String { pub fn msg_title_error(_key_config: &SharedKeyConfig) -> String { "Error".to_string() } +pub fn msg_title_info(_key_config: &SharedKeyConfig) -> String { + "Info".to_string() +} pub fn commit_title(_key_config: &SharedKeyConfig) -> String { "Commit".to_string() } @@ -596,4 +599,11 @@ pub mod commands { CMD_GROUP_GENERAL, ) } + pub fn status_push(key_config: &SharedKeyConfig) -> CommandText { + CommandText::new( + format!("Push [{}]", get_hint(key_config.push),), + "push to origin", + CMD_GROUP_GENERAL, + ) + } } diff --git a/src/tabs/status.rs b/src/tabs/status.rs index 120f159ac1..69ba6c79da 100644 --- a/src/tabs/status.rs +++ b/src/tabs/status.rs @@ -323,6 +323,24 @@ impl Status { true } } + + fn push(&self) { + if let Some(branch) = self.index_wd.branch_name() { + let branch = format!("refs/heads/{}", branch); + if let Err(e) = sync::push_origin(CWD, branch.as_str()) { + self.queue.borrow_mut().push_back( + InternalEvent::ShowErrorMsg(format!( + "push failed:\n{}", + e + )), + ); + } else { + self.queue.borrow_mut().push_back( + InternalEvent::ShowInfoMsg("pushed".to_string()), + ); + } + } + } } impl Component for Status { @@ -369,6 +387,11 @@ impl Component for Status { true, true, )); + out.push(CommandInfo::new( + strings::commands::status_push(&self.key_config), + self.index_wd.branch_name().is_some(), + true, + )); out.push( CommandInfo::new( @@ -451,6 +474,9 @@ impl Component for Status { .borrow_mut() .push_back(InternalEvent::CreateBranch); Ok(true) + } else if k == self.key_config.push { + self.push(); + Ok(true) } else { Ok(false) };