Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* execute git-hooks directly if possible (on *nix) else use sh instead of bash (without reading SHELL variable) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483))

### Added
* Support pre-push hook [[@xlai89](https://github.com/xlai89)] ([#1933](https://github.com/extrawurst/gitui/issues/1933))
* Files and status tab support pageUp and pageDown [[@fatpandac](https://github.com/fatpandac)] ([#1951](https://github.com/extrawurst/gitui/issues/1951))
* support loading custom syntax highlighting themes from a file [[@acuteenvy](https://github.com/acuteenvy)] ([#2565](https://github.com/gitui-org/gitui/pull/2565))
* Select syntax highlighting theme out of the defaults from syntect [[@vasilismanol](https://github.com/vasilismanol)] ([#1931](https://github.com/extrawurst/gitui/issues/1931))
Expand Down
9 changes: 9 additions & 0 deletions asyncgit/src/sync/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ pub fn hooks_prepare_commit_msg(
.into())
}

/// see `git2_hooks::hooks_pre_push`
pub fn hooks_pre_push(repo_path: &RepoPath) -> Result<HookResult> {
scope_time!("hooks_pre_push");

let repo = repo(repo_path)?;

Ok(git2_hooks::hooks_pre_push(&repo, None)?.into())
}

#[cfg(test)]
mod tests {
use std::{ffi::OsString, io::Write as _, path::Path};
Expand Down
3 changes: 2 additions & 1 deletion asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ pub use diff::get_diff_commit;
pub use git2::BranchType;
pub use hooks::{
hooks_commit_msg, hooks_post_commit, hooks_pre_commit,
hooks_prepare_commit_msg, HookResult, PrepareCommitMsgSource,
hooks_pre_push, hooks_prepare_commit_msg, HookResult,
PrepareCommitMsgSource,
};
pub use hunks::{reset_hunk, stage_hunk, unstage_hunk};
pub use ignore::add_to_ignore;
Expand Down
48 changes: 48 additions & 0 deletions git2-hooks/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub const HOOK_POST_COMMIT: &str = "post-commit";
pub const HOOK_PRE_COMMIT: &str = "pre-commit";
pub const HOOK_COMMIT_MSG: &str = "commit-msg";
pub const HOOK_PREPARE_COMMIT_MSG: &str = "prepare-commit-msg";
pub const HOOK_PRE_PUSH: &str = "pre-push";

const HOOK_COMMIT_MSG_TEMP_FILE: &str = "COMMIT_EDITMSG";

Expand Down Expand Up @@ -169,6 +170,20 @@ pub fn hooks_post_commit(
hook.run_hook(&[])
}

/// this hook is documented here <https://git-scm.com/docs/githooks#_pre_push>
pub fn hooks_pre_push(
repo: &Repository,
other_paths: Option<&[&str]>,
) -> Result<HookResult> {
let hook = HookPaths::new(repo, other_paths, HOOK_PRE_PUSH)?;

if !hook.found() {
return Ok(HookResult::NoHookFound);
}

hook.run_hook(&[])
}

pub enum PrepareCommitMsgSource {
Message,
Template,
Expand Down Expand Up @@ -657,4 +672,37 @@ exit 2
)
);
}

#[test]
fn test_pre_push_sh() {
let (_td, repo) = repo_init();

let hook = b"#!/bin/sh
exit 0
";

create_hook(&repo, HOOK_PRE_PUSH, hook);

let res = hooks_pre_push(&repo, None).unwrap();

assert!(matches!(res, HookResult::Ok { .. }));
}

#[test]
fn test_pre_push_fail_sh() {
let (_td, repo) = repo_init();

let hook = b"#!/bin/sh
echo 'failed'
exit 3
";
create_hook(&repo, HOOK_PRE_PUSH, hook);
let res = hooks_pre_push(&repo, None).unwrap();
let HookResult::RunNotSuccessful { code, stdout, .. } = res
else {
unreachable!()
};
assert_eq!(code.unwrap(), 3);
assert_eq!(&stdout, "failed\n");
}
}
17 changes: 15 additions & 2 deletions src/popups/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use asyncgit::{
extract_username_password_for_push,
need_username_password_for_push, BasicAuthCredential,
},
get_branch_remote,
get_branch_remote, hooks_pre_push,
remotes::get_default_remote_for_push,
RepoPathRef,
HookResult, RepoPathRef,
},
AsyncGitNotification, AsyncPush, PushRequest, PushType,
RemoteProgress, RemoteProgressState,
Expand Down Expand Up @@ -144,6 +144,19 @@ impl PushPopup {
remote
};

// run pre push hook - can reject push
if let HookResult::NotOk(e) =
hooks_pre_push(&self.repo.borrow())?
{
log::error!("pre-push hook failed: {e}");
self.queue.push(InternalEvent::ShowErrorMsg(format!(
"pre-push hook failed:\n{e}"
)));
self.pending = false;
self.visible = false;
return Ok(());
}

self.pending = true;
self.progress = None;
self.git_push.request(PushRequest {
Expand Down
17 changes: 15 additions & 2 deletions src/popups/push_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use asyncgit::{
extract_username_password, need_username_password,
BasicAuthCredential,
},
get_default_remote, AsyncProgress, PushTagsProgress,
RepoPathRef,
get_default_remote, hooks_pre_push, AsyncProgress,
HookResult, PushTagsProgress, RepoPathRef,
},
AsyncGitNotification, AsyncPushTags, PushTagsRequest,
};
Expand Down Expand Up @@ -84,6 +84,19 @@ impl PushTagsPopup {
&mut self,
cred: Option<BasicAuthCredential>,
) -> Result<()> {
// run pre push hook - can reject push
if let HookResult::NotOk(e) =
hooks_pre_push(&self.repo.borrow())?
{
log::error!("pre-push hook failed: {e}");
self.queue.push(InternalEvent::ShowErrorMsg(format!(
"pre-push hook failed:\n{e}"
)));
self.pending = false;
self.visible = false;
return Ok(());
}

self.pending = true;
self.progress = None;
self.git_push.request(PushTagsRequest {
Expand Down