Skip to content

Commit

Permalink
adding pre-commit hook (#386)
Browse files Browse the repository at this point in the history
see #313
  • Loading branch information
pm100 authored Nov 1, 2020
1 parent bbc0488 commit 99c3277
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 18 deletions.
102 changes: 95 additions & 7 deletions asyncgit/src/sync/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{
};

const HOOK_POST_COMMIT: &str = ".git/hooks/post-commit";
const HOOK_PRE_COMMIT: &str = ".git/hooks/pre-commit";
const HOOK_COMMIT_MSG: &str = ".git/hooks/commit-msg";
const HOOK_COMMIT_MSG_TEMP_FILE: &str = ".git/COMMIT_EDITMSG";

Expand Down Expand Up @@ -45,6 +46,21 @@ pub fn hooks_commit_msg(
}
}

/// this hook is documented here https://git-scm.com/docs/githooks#_pre_commit
///
pub fn hooks_pre_commit(repo_path: &str) -> Result<HookResult> {
scope_time!("hooks_pre_commit");

let work_dir = work_dir_as_string(repo_path)?;

if hook_runable(work_dir.as_str(), HOOK_PRE_COMMIT) {
let res = run_hook(work_dir.as_str(), HOOK_PRE_COMMIT, &[]);

Ok(res)
} else {
Ok(HookResult::Ok)
}
}
///
pub fn hooks_post_commit(repo_path: &str) -> Result<HookResult> {
scope_time!("hooks_post_commit");
Expand Down Expand Up @@ -94,13 +110,8 @@ fn run_hook(
hook_script: &str,
args: &[&str],
) -> HookResult {
let mut bash_args = vec![hook_script.to_string()];
bash_args.extend_from_slice(
&args
.iter()
.map(|x| (*x).to_string())
.collect::<Vec<String>>(),
);
let arg_str = format!("{} {}", hook_script, args.join(" "));
let bash_args = vec!["-c".to_string(), arg_str];

let output = Command::new("bash")
.args(bash_args)
Expand Down Expand Up @@ -204,6 +215,83 @@ exit 0
assert_eq!(msg, String::from("test"));
}

#[test]
fn test_pre_commit_sh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();

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

create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert_eq!(res, HookResult::Ok);
}

#[test]
fn test_pre_commit_fail_sh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();

let hook = b"#!/bin/sh
echo 'rejected'
exit 1
";

create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert!(res != HookResult::Ok);
}

#[test]
fn test_pre_commit_py() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();

// mirror how python pre-commmit sets itself up
#[cfg(not(windows))]
let hook = b"#!/usr/bin/env python
import sys
sys.exit(0)
";
#[cfg(windows)]
let hook = b"#!/bin/env python.exe
import sys
sys.exit(0)
";

create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert_eq!(res, HookResult::Ok);
}

#[test]
fn test_pre_commit_fail_py() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();

// mirror how python pre-commmit sets itself up
#[cfg(not(windows))]
let hook = b"#!/usr/bin/env python
import sys
sys.exit(1)
";
#[cfg(windows)]
let hook = b"#!/bin/env python.exe
import sys
sys.exit(1)
";

create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert!(res != HookResult::Ok);
}

#[test]
fn test_hooks_commit_msg_reject() {
let (_td, repo) = repo_init().unwrap();
Expand Down
4 changes: 3 additions & 1 deletion asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ pub use commit_details::{
pub use commit_files::get_commit_files;
pub use commits_info::{get_commits_info, CommitId, CommitInfo};
pub use diff::get_diff_commit;
pub use hooks::{hooks_commit_msg, hooks_post_commit, HookResult};
pub use hooks::{
hooks_commit_msg, hooks_post_commit, hooks_pre_commit, HookResult,
};
pub use hunks::{reset_hunk, stage_hunk, unstage_hunk};
pub use ignore::add_to_ignore;
pub use logwalker::LogWalker;
Expand Down
10 changes: 10 additions & 0 deletions src/components/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@ impl CommitComponent {
}

fn commit_msg(&mut self, msg: String) -> Result<()> {
if let HookResult::NotOk(e) = sync::hooks_pre_commit(CWD)? {
log::error!("pre-commit hook error: {}", e);
self.queue.borrow_mut().push_back(
InternalEvent::ShowErrorMsg(format!(
"pre-commit hook error:\n{}",
e
)),
);
return Ok(());
}
let mut msg = msg;
if let HookResult::NotOk(e) =
sync::hooks_commit_msg(CWD, &mut msg)?
Expand Down
33 changes: 23 additions & 10 deletions src/components/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ use super::{
};
use crate::{keys::SharedKeyConfig, strings, ui};
use crossterm::event::Event;
use std::convert::TryFrom;
use tui::{
backend::Backend,
layout::{Alignment, Rect},
text::{Span, Spans},
text::Span,
widgets::{Block, BorderType, Borders, Clear, Paragraph, Wrap},
Frame,
};
use ui::style::SharedTheme;

pub struct MsgComponent {
title: String,
msg: String,
Expand All @@ -32,17 +32,30 @@ impl DrawableComponent for MsgComponent {
if !self.visible {
return Ok(());
}
let txt = Spans::from(
self.msg
.split('\n')
.map(|string| Span::raw::<String>(string.to_string()))
.collect::<Vec<Span>>(),
);

let area = ui::centered_rect_absolute(65, 25, f.size());
// determine the maximum width of text block
let lens = self
.msg
.split('\n')
.map(str::len)
.collect::<Vec<usize>>();
let mut max = lens.iter().max().expect("max") + 2;
if max > std::u16::MAX as usize {
max = std::u16::MAX as usize;
}
let mut width =
u16::try_from(max).expect("cant fail due to check above");
// dont overflow screen, and dont get too narrow
if width > f.size().width {
width = f.size().width
} else if width < 60 {
width = 60
}

let area = ui::centered_rect_absolute(width, 25, f.size());
f.render_widget(Clear, area);
f.render_widget(
Paragraph::new(txt)
Paragraph::new(self.msg.clone())
.block(
Block::default()
.title(Span::styled(
Expand Down

0 comments on commit 99c3277

Please sign in to comment.