mirror of
https://github.com/gitui-org/gitui
synced 2026-05-23 00:48:35 +00:00
feat: support pre-push hooks (#2737)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
This commit is contained in:
parent
2374e00302
commit
cb17cfe105
6 changed files with 90 additions and 5 deletions
|
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
* increase MSRV from 1.81 to 1.82 [[@cruessler](https://github.com/cruessler)]
|
||||
|
||||
### Added
|
||||
* Support pre-push hook [[@xlai89](https://github.com/xlai89)] ([#1933](https://github.com/extrawurst/gitui/issues/1933))
|
||||
* Message tab supports pageUp and pageDown [[@xlai89](https://github.com/xlai89)] ([#2623](https://github.com/extrawurst/gitui/issues/2623))
|
||||
* 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))
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,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";
|
||||
|
||||
|
|
@ -170,6 +171,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,
|
||||
|
|
@ -658,4 +673,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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue