
The notifications from git output could take up variable amounts of screen space, and they were quite obnoxious when a git command printed lots of output, such as fetching many new branches or verbose push hooks. This change makes the push/pull/fetch buttons trigger a small notification toast, based on the output of the command that was ran. For errors or commands with more output the user may want to see, there's an "Open Log" button which opens a new buffer with the output of that command. It also uses this behavior for long error notifications for other git commands like `commit` and `checkout`. The output of those commands can be quite long due to arbitrary githooks running. Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
152 lines
5.5 KiB
Rust
152 lines
5.5 KiB
Rust
use anyhow::Context as _;
|
|
use git::repository::{Remote, RemoteCommandOutput};
|
|
use linkify::{LinkFinder, LinkKind};
|
|
use ui::SharedString;
|
|
use util::ResultExt as _;
|
|
|
|
#[derive(Clone)]
|
|
pub enum RemoteAction {
|
|
Fetch,
|
|
Pull(Remote),
|
|
Push(SharedString, Remote),
|
|
}
|
|
|
|
impl RemoteAction {
|
|
pub fn name(&self) -> &'static str {
|
|
match self {
|
|
RemoteAction::Fetch => "fetch",
|
|
RemoteAction::Pull(_) => "pull",
|
|
RemoteAction::Push(_, _) => "push",
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum SuccessStyle {
|
|
Toast,
|
|
ToastWithLog { output: RemoteCommandOutput },
|
|
PushPrLink { link: String },
|
|
}
|
|
|
|
pub struct SuccessMessage {
|
|
pub message: String,
|
|
pub style: SuccessStyle,
|
|
}
|
|
|
|
pub fn format_output(action: &RemoteAction, output: RemoteCommandOutput) -> SuccessMessage {
|
|
match action {
|
|
RemoteAction::Fetch => {
|
|
if output.stderr.is_empty() {
|
|
SuccessMessage {
|
|
message: "Already up to date".into(),
|
|
style: SuccessStyle::Toast,
|
|
}
|
|
} else {
|
|
SuccessMessage {
|
|
message: "Synchronized with remotes".into(),
|
|
style: SuccessStyle::ToastWithLog { output },
|
|
}
|
|
}
|
|
}
|
|
RemoteAction::Pull(remote_ref) => {
|
|
let get_changes = |output: &RemoteCommandOutput| -> anyhow::Result<u32> {
|
|
let last_line = output
|
|
.stdout
|
|
.lines()
|
|
.last()
|
|
.context("Failed to get last line of output")?
|
|
.trim();
|
|
|
|
let files_changed = last_line
|
|
.split_whitespace()
|
|
.next()
|
|
.context("Failed to get first word of last line")?
|
|
.parse()?;
|
|
|
|
Ok(files_changed)
|
|
};
|
|
|
|
if output.stderr.starts_with("Everything up to date") {
|
|
SuccessMessage {
|
|
message: output.stderr.trim().to_owned(),
|
|
style: SuccessStyle::Toast,
|
|
}
|
|
} else if output.stdout.starts_with("Updating") {
|
|
let files_changed = get_changes(&output).log_err();
|
|
let message = if let Some(files_changed) = files_changed {
|
|
format!(
|
|
"Received {} file change{} from {}",
|
|
files_changed,
|
|
if files_changed == 1 { "" } else { "s" },
|
|
remote_ref.name
|
|
)
|
|
} else {
|
|
format!("Fast forwarded from {}", remote_ref.name)
|
|
};
|
|
SuccessMessage {
|
|
message,
|
|
style: SuccessStyle::ToastWithLog { output },
|
|
}
|
|
} else if output.stdout.starts_with("Merge") {
|
|
let files_changed = get_changes(&output).log_err();
|
|
let message = if let Some(files_changed) = files_changed {
|
|
format!(
|
|
"Merged {} file change{} from {}",
|
|
files_changed,
|
|
if files_changed == 1 { "" } else { "s" },
|
|
remote_ref.name
|
|
)
|
|
} else {
|
|
format!("Merged from {}", remote_ref.name)
|
|
};
|
|
SuccessMessage {
|
|
message,
|
|
style: SuccessStyle::ToastWithLog { output },
|
|
}
|
|
} else if output.stdout.contains("Successfully rebased") {
|
|
SuccessMessage {
|
|
message: format!("Successfully rebased from {}", remote_ref.name),
|
|
style: SuccessStyle::ToastWithLog { output },
|
|
}
|
|
} else {
|
|
SuccessMessage {
|
|
message: format!("Successfully pulled from {}", remote_ref.name),
|
|
style: SuccessStyle::ToastWithLog { output },
|
|
}
|
|
}
|
|
}
|
|
RemoteAction::Push(branch_name, remote_ref) => {
|
|
if output.stderr.contains("* [new branch]") {
|
|
let style = if output.stderr.contains("Create a pull request") {
|
|
let finder = LinkFinder::new();
|
|
let first_link = finder
|
|
.links(&output.stderr)
|
|
.filter(|link| *link.kind() == LinkKind::Url)
|
|
.map(|link| link.start()..link.end())
|
|
.next();
|
|
if let Some(link) = first_link {
|
|
let link = output.stderr[link].to_string();
|
|
SuccessStyle::PushPrLink { link }
|
|
} else {
|
|
SuccessStyle::ToastWithLog { output }
|
|
}
|
|
} else {
|
|
SuccessStyle::ToastWithLog { output }
|
|
};
|
|
SuccessMessage {
|
|
message: format!("Published {} to {}", branch_name, remote_ref.name),
|
|
style,
|
|
}
|
|
} else if output.stderr.starts_with("Everything up to date") {
|
|
SuccessMessage {
|
|
message: output.stderr.trim().to_owned(),
|
|
style: SuccessStyle::Toast,
|
|
}
|
|
} else {
|
|
SuccessMessage {
|
|
message: "Successfully pushed new branch".to_owned(),
|
|
style: SuccessStyle::ToastWithLog { output },
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|