Skip to content

Commit 3a1148e

Browse files
authored
Merge pull request #200 from blairconrad/alternative-output
Make output more human-readable
2 parents 696db7b + 8ef9791 commit 3a1148e

File tree

2 files changed

+194
-16
lines changed

2 files changed

+194
-16
lines changed

src/lib.rs

Lines changed: 175 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod config;
77
mod owned;
88
mod stack;
99

10+
use git2::DiffStats;
1011
use std::io::Write;
1112
use std::path::Path;
1213

@@ -337,7 +338,10 @@ fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository
337338
&head_tree,
338339
&[&head_commit],
339340
)?)?;
340-
announce(logger, Announcement::Committed(&head_commit, &diff));
341+
announce(
342+
logger,
343+
Announcement::Committed(&head_commit, dest_commit_locator, &diff),
344+
);
341345
} else {
342346
announce(
343347
logger,
@@ -572,7 +576,7 @@ fn index_stats(repo: &git2::Repository) -> Result<git2::DiffStats> {
572576

573577
// Messages that will be shown to users during normal operations (not debug messages).
574578
enum Announcement<'r> {
575-
Committed(&'r git2::Commit<'r>, &'r git2::DiffStats),
579+
Committed(&'r git2::Commit<'r>, &'r str, &'r git2::DiffStats),
576580
WouldHaveCommitted(&'r str, &'r git2::DiffStats),
577581
WouldHaveRebased(&'r std::process::Command),
578582
HowToSquash(String),
@@ -592,17 +596,26 @@ enum Announcement<'r> {
592596

593597
fn announce(logger: &slog::Logger, announcement: Announcement) {
594598
match announcement {
595-
Announcement::Committed(commit, diff) => info!(
596-
logger,
597-
"committed";
598-
"commit" => &commit.id().to_string(),
599-
"header" => format!("+{},-{}", &diff.insertions(), &diff.deletions())
600-
),
599+
Announcement::Committed(commit, destination, diff) => {
600+
let commit_short_id = commit.as_object().short_id().unwrap();
601+
let commit_short_id = commit_short_id
602+
.as_str()
603+
.expect("the commit short id is always a valid ASCII string");
604+
let change_header = format_change_header(diff);
605+
606+
info!(
607+
logger,
608+
"committed";
609+
"fixup" => destination,
610+
"commit" => commit_short_id,
611+
"header" => change_header,
612+
);
613+
}
601614
Announcement::WouldHaveCommitted(fixup, diff) => info!(
602615
logger,
603616
"would have committed";
604617
"fixup" => fixup,
605-
"header" => format!("+{},-{}", &diff.insertions(), &diff.deletions())
618+
"header" => format_change_header(diff),
606619
),
607620
Announcement::WouldHaveRebased(command) => info!(
608621
logger, "would have run git rebase"; "command" => format!("{:?}", command)
@@ -674,11 +687,45 @@ fn announce(logger: &slog::Logger, announcement: Announcement) {
674687
}
675688
}
676689

690+
fn format_change_header(diff: &DiffStats) -> String {
691+
let insertions = diff.insertions();
692+
let deletions = diff.deletions();
693+
694+
let mut header = String::new();
695+
if insertions > 0 {
696+
header.push_str(&format!(
697+
"{} {}(+)",
698+
insertions,
699+
if insertions == 1 {
700+
"insertion"
701+
} else {
702+
"insertions"
703+
}
704+
));
705+
}
706+
if deletions > 0 {
707+
if !header.is_empty() {
708+
header.push_str(", ");
709+
}
710+
header.push_str(&format!(
711+
"{} {}(-)",
712+
deletions,
713+
if deletions == 1 {
714+
"deletion"
715+
} else {
716+
"deletions"
717+
}
718+
));
719+
}
720+
header
721+
}
722+
677723
#[cfg(test)]
678724
mod tests {
679725
use git2::message_trailers_strs;
680726
use serde_json::json;
681727
use std::path::PathBuf;
728+
use tests::repo_utils::add;
682729

683730
use super::*;
684731
mod log_utils;
@@ -732,8 +779,113 @@ mod tests {
732779
log_utils::assert_log_messages_are(
733780
capturing_logger.visible_logs(),
734781
vec![
735-
&json!({"level": "INFO", "msg": "committed"}),
736-
&json!({"level": "INFO", "msg": "committed"}),
782+
&json!({
783+
"level": "INFO",
784+
"msg": "committed",
785+
"fixup": "Initial commit.",
786+
"header": "1 insertion(+)",
787+
}),
788+
&json!({
789+
"level": "INFO",
790+
"msg": "committed",
791+
"fixup": "Initial commit.",
792+
"header": "2 insertions(+)",
793+
}),
794+
&json!({
795+
"level": "INFO",
796+
"msg": "To squash the new commits, rebase:",
797+
"command": "git rebase --interactive --autosquash --autostash --root",
798+
}),
799+
],
800+
);
801+
}
802+
803+
#[test]
804+
fn one_deletion() {
805+
let (ctx, file_path) = repo_utils::prepare_repo();
806+
std::fs::write(
807+
ctx.join(&file_path),
808+
br#"
809+
line
810+
line
811+
"#,
812+
)
813+
.unwrap();
814+
add(&ctx.repo, &file_path);
815+
816+
let actual_pre_absorb_commit = ctx.repo.head().unwrap().peel_to_commit().unwrap().id();
817+
818+
// run 'git-absorb'
819+
let mut capturing_logger = log_utils::CapturingLogger::new();
820+
run_with_repo(&capturing_logger.logger, &DEFAULT_CONFIG, &ctx.repo).unwrap();
821+
822+
let mut revwalk = ctx.repo.revwalk().unwrap();
823+
revwalk.push_head().unwrap();
824+
assert_eq!(revwalk.count(), 2);
825+
826+
assert!(nothing_left_in_index(&ctx.repo).unwrap());
827+
828+
let pre_absorb_ref_commit = ctx.repo.refname_to_id("PRE_ABSORB_HEAD").unwrap();
829+
assert_eq!(pre_absorb_ref_commit, actual_pre_absorb_commit);
830+
831+
log_utils::assert_log_messages_are(
832+
capturing_logger.visible_logs(),
833+
vec![
834+
&json!({
835+
"level": "INFO",
836+
"msg": "committed",
837+
"fixup": "Initial commit.",
838+
"header": "3 deletions(-)",
839+
}),
840+
&json!({
841+
"level": "INFO",
842+
"msg": "To squash the new commits, rebase:",
843+
"command": "git rebase --interactive --autosquash --autostash --root",
844+
}),
845+
],
846+
);
847+
}
848+
849+
#[test]
850+
fn one_insertion_and_one_deletion() {
851+
let (ctx, file_path) = repo_utils::prepare_repo();
852+
std::fs::write(
853+
ctx.join(&file_path),
854+
br#"
855+
line
856+
line
857+
858+
even more
859+
lines
860+
"#,
861+
)
862+
.unwrap();
863+
add(&ctx.repo, &file_path);
864+
865+
let actual_pre_absorb_commit = ctx.repo.head().unwrap().peel_to_commit().unwrap().id();
866+
867+
// run 'git-absorb'
868+
let mut capturing_logger = log_utils::CapturingLogger::new();
869+
run_with_repo(&capturing_logger.logger, &DEFAULT_CONFIG, &ctx.repo).unwrap();
870+
871+
let mut revwalk = ctx.repo.revwalk().unwrap();
872+
revwalk.push_head().unwrap();
873+
assert_eq!(revwalk.count(), 2);
874+
875+
assert!(nothing_left_in_index(&ctx.repo).unwrap());
876+
877+
let pre_absorb_ref_commit = ctx.repo.refname_to_id("PRE_ABSORB_HEAD").unwrap();
878+
assert_eq!(pre_absorb_ref_commit, actual_pre_absorb_commit);
879+
880+
log_utils::assert_log_messages_are(
881+
capturing_logger.visible_logs(),
882+
vec![
883+
&json!({
884+
"level": "INFO",
885+
"msg": "committed",
886+
"fixup": "Initial commit.",
887+
"header": "1 insertion(+), 1 deletion(-)",
888+
}),
737889
&json!({
738890
"level": "INFO",
739891
"msg": "To squash the new commits, rebase:",
@@ -1165,7 +1317,12 @@ mod tests {
11651317
log_utils::assert_log_messages_are(
11661318
capturing_logger.visible_logs(),
11671319
vec![
1168-
&json!({"level": "INFO", "msg": "committed"}),
1320+
&json!({
1321+
"level": "INFO",
1322+
"msg": "committed",
1323+
"fixup": "Initial commit.",
1324+
"header": "3 insertions(+)",
1325+
}),
11691326
&json!({
11701327
"level": "INFO",
11711328
"msg": "To squash the new commits, rebase:",
@@ -1600,11 +1757,15 @@ mod tests {
16001757
vec![
16011758
&json!({
16021759
"level": "INFO",
1603-
"msg": "would have committed", "fixup": "Initial commit.",
1760+
"msg": "would have committed",
1761+
"fixup": "Initial commit.",
1762+
"header": "1 insertion(+)",
16041763
}),
16051764
&json!({
16061765
"level": "INFO",
1607-
"msg": "would have committed", "fixup": "Initial commit.",
1766+
"msg": "would have committed",
1767+
"fixup": "Initial commit.",
1768+
"header": "2 insertions(+)",
16081769
}),
16091770
],
16101771
);

src/main.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ extern crate slog;
44
use clap::{CommandFactory, Parser as _};
55
use clap_complete::{generate, Shell};
66
use clap_complete_nushell::Nushell;
7-
use slog::Drain;
7+
use slog::{Drain, Record};
8+
use slog_term::{CountingWriter, RecordDecorator, ThreadSafeTimestampFn};
89
use std::io;
10+
use std::io::Write;
911

1012
/// Automatically absorb staged changes into your current branch
1113
#[derive(Debug, clap::Parser)]
@@ -85,7 +87,10 @@ fn main() {
8587
}
8688

8789
let decorator = slog_term::TermDecorator::new().build();
88-
let drain = slog_term::FullFormat::new(decorator).build().fuse();
90+
let drain = slog_term::FullFormat::new(decorator)
91+
.use_custom_header_print(print_msg_header)
92+
.build()
93+
.fuse();
8994
let drain = std::sync::Mutex::new(drain).fuse();
9095

9196
let drain = slog::LevelFilter::new(
@@ -127,3 +132,15 @@ fn main() {
127132
::std::process::exit(1);
128133
}
129134
}
135+
136+
pub fn print_msg_header(
137+
_fn_timestamp: &dyn ThreadSafeTimestampFn<Output = io::Result<()>>,
138+
mut rd: &mut dyn RecordDecorator,
139+
record: &Record,
140+
_use_file_location: bool,
141+
) -> io::Result<bool> {
142+
rd.start_level()?; // color-codes the message
143+
let mut count_rd = CountingWriter::new(&mut rd);
144+
write!(count_rd, "{}", record.msg())?;
145+
Ok(count_rd.count() != 0)
146+
}

0 commit comments

Comments
 (0)