Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
* support 'n'/'p' key to move to the next/prev hunk in diff component [[@hamflx](https://github.com/hamflx)] ([#1523](https://github.com/extrawurst/gitui/issues/1523))
* simplify theme overrides [[@cruessler](https://github.com/cruessler)] ([#1367](https://github.com/extrawurst/gitui/issues/1367))
* Support for sign-off of commits [#1757]https://github.com/extrawurst/gitui/issues/1757

### Fixes
* fix commit dialog char count for multibyte characters ([#1726](https://github.com/extrawurst/gitui/issues/1726))
Expand Down
57 changes: 53 additions & 4 deletions asyncgit/src/sync/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,22 @@ pub(crate) fn signature_allow_undefined_name(
signature
}

/// this does not run any git hooks, git-hooks have to be executed manually, checkout `hooks_commit_msg` for example
pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
fn add_sign_off(msg: &str, signature: &Signature) -> String {
match (signature.name(), signature.email()) {
(Some(name), Some(mail)) => {
let mut msg = msg.to_owned();
msg.push_str(&format!("\nSigned-off-by {name} <{mail}>"));
msg
}
_ => msg.to_owned(),
}
}

fn do_commit(
repo_path: &RepoPath,
msg: &str,
sign_off: bool,
) -> Result<CommitId> {
scope_time!("commit");

let repo = repo(repo_path)?;
Expand All @@ -79,18 +93,39 @@ pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {

let parents = parents.iter().collect::<Vec<_>>();

let msg = if sign_off {
add_sign_off(msg, &signature)
} else {
msg.to_owned()
};

Ok(repo
.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&msg,
&tree,
parents.as_slice(),
)?
.into())
}

/// this does not run any git hooks, git-hooks have to be executed manually, checkout `hooks_commit_msg` for example
pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
do_commit(repo_path, msg, false)
}

/// Do a commit including the sign-off option
///
/// Refer to [git documentation](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s)
pub fn commit_with_signoff(
repo_path: &RepoPath,
msg: &str,
) -> Result<CommitId> {
do_commit(repo_path, msg, true)
}

/// Tag a commit.
///
/// This function will return an `Err(…)` variant if the tag’s name is refused
Expand Down Expand Up @@ -133,9 +168,11 @@ mod tests {
LogWalker,
};
use commit::{amend, tag_commit};
use git2::Repository;
use git2::{Repository, Signature};
use std::{fs::File, io::Write, path::Path};

use super::add_sign_off;

fn count_commits(repo: &Repository, max: usize) -> usize {
let mut items = Vec::new();
let mut walk = LogWalker::new(repo, max).unwrap();
Expand Down Expand Up @@ -384,4 +421,16 @@ mod tests {

Ok(())
}

#[test]
fn test_add_sign_off_to_commit_msg() {
let in_msg = "test commit";
let signature =
Signature::now("MyName", "[email protected]").unwrap();

let expected =
format!("{in_msg}\nSigned-off-by MyName <[email protected]>");
let result = add_sign_off(in_msg, &signature);
assert_eq!(expected, result);
}
}
2 changes: 1 addition & 1 deletion asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub use branch::{
merge_rebase::merge_upstream_rebase, rename::rename_branch,
validate_branch_name, BranchCompare, BranchDetails, BranchInfo,
};
pub use commit::{amend, commit, tag_commit};
pub use commit::{amend, commit, commit_with_signoff, tag_commit};
pub use commit_details::{
get_commit_details, CommitDetails, CommitMessage, CommitSignature,
};
Expand Down
24 changes: 24 additions & 0 deletions src/components/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum Mode {
Merge(Vec<CommitId>),
Revert,
Reword(CommitId),
Signoff,
}

pub struct CommitComponent {
Expand Down Expand Up @@ -301,6 +302,9 @@ impl CommitComponent {

commit
}
Mode::Signoff => {
sync::commit_with_signoff(&self.repo.borrow(), msg)?
}
};
Ok(())
}
Expand Down Expand Up @@ -341,6 +345,15 @@ impl CommitComponent {

Ok(())
}
fn toggle_signoff(&mut self) {
if matches!(self.mode, Mode::Normal) {
self.mode = Mode::Signoff;
self.input.set_title(strings::commit_title_signoff());
} else {
self.mode = Mode::Normal;
self.input.set_title(strings::commit_title());
}
}
fn toggle_verify(&mut self) {
self.verify = !self.verify;
}
Expand Down Expand Up @@ -464,6 +477,12 @@ impl Component for CommitComponent {
true,
));

out.push(CommandInfo::new(
strings::commands::commit_signoff(&self.key_config),
true,
true,
));

out.push(CommandInfo::new(
strings::commands::commit_open_editor(
&self.key_config,
Expand Down Expand Up @@ -531,6 +550,11 @@ impl Component for CommitComponent {
self.input.set_text(msg);
self.commit_msg_history_idx += 1;
}
} else if key_match(
e,
self.key_config.keys.toggle_signoff,
) {
self.toggle_signoff();
}
// stop key event propagation
return Ok(EventState::Consumed);
Expand Down
2 changes: 2 additions & 0 deletions src/keys/key_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub struct KeysList {
pub log_reset_comit: GituiKeyEvent,
pub log_reword_comit: GituiKeyEvent,
pub commit_amend: GituiKeyEvent,
pub toggle_signoff: GituiKeyEvent,
pub toggle_verify: GituiKeyEvent,
pub copy: GituiKeyEvent,
pub create_branch: GituiKeyEvent,
Expand Down Expand Up @@ -174,6 +175,7 @@ impl Default for KeysList {
log_reset_comit: GituiKeyEvent { code: KeyCode::Char('R'), modifiers: KeyModifiers::SHIFT },
log_reword_comit: GituiKeyEvent { code: KeyCode::Char('r'), modifiers: KeyModifiers::empty() },
commit_amend: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
toggle_signoff: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL),
toggle_verify: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL),
copy: GituiKeyEvent::new(KeyCode::Char('y'), KeyModifiers::empty()),
create_branch: GituiKeyEvent::new(KeyCode::Char('c'), KeyModifiers::empty()),
Expand Down
15 changes: 15 additions & 0 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ pub fn commit_title_revert() -> String {
pub fn commit_title_amend() -> String {
"Commit (Amend)".to_string()
}
pub fn commit_title_signoff() -> String {
"Commit (Sign-off)".to_string()
}
pub fn commit_msg(_key_config: &SharedKeyConfig) -> String {
"type commit message..".to_string()
}
Expand Down Expand Up @@ -980,6 +983,18 @@ pub mod commands {
CMD_GROUP_COMMIT_POPUP,
)
}
pub fn commit_signoff(
key_config: &SharedKeyConfig,
) -> CommandText {
CommandText::new(
format!(
"Sing-off [{}]",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthiasbeyer this PR is already merged, could you open a PR fixing the typos?

key_config.get_hint(key_config.keys.toggle_signoff),
),
"sign-off commit (-s option)",
CMD_GROUP_COMMIT_POPUP,
)
}
pub fn edit_item(key_config: &SharedKeyConfig) -> CommandText {
CommandText::new(
format!(
Expand Down