visualize outgoing/incoming changes (#364)

This commit is contained in:
Stephan Dilly 2020-10-25 17:35:54 +01:00 committed by GitHub
parent a2b26c30dc
commit 4ee4432f16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 91 additions and 9 deletions

View file

@ -12,11 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- added gitui to [chocolatey](https://chocolatey.org/packages/gitui) on windows by [@nils-a](https://github.com/nils-a)
- push to remote ([#265](https://github.com/extrawurst/gitui/issues/265)) ([#267](https://github.com/extrawurst/gitui/issues/267))
![branches](assets/push.gif)
![push](assets/push.gif)
- incoming/outgoing commits to upstream ([#362](https://github.com/extrawurst/gitui/issues/362))
- new branch list popup incl. checkout/delete/rename [[@WizardOhio24](https://github.com/WizardOhio24)] ([#303](https://github.com/extrawurst/gitui/issues/303)) ([#323](https://github.com/extrawurst/gitui/issues/323))
![push](assets/branches.gif)
![branches](assets/branches.gif)
- scrollbar in long commit messages [[@timaliberdov](https://github.com/timaliberdov)] ([#308](https://github.com/extrawurst/gitui/issues/308))

View file

@ -2,14 +2,12 @@
use crate::{
error::{Error, Result},
sync::utils,
sync::{utils, CommitId},
};
use git2::BranchType;
use scopetime::scope_time;
use utils::get_head_repo;
use super::CommitId;
/// returns the branch-name head is currently pointing to
/// this might be expensive, see `cached::BranchName`
pub(crate) fn get_branch_name(repo_path: &str) -> Result<String> {
@ -79,6 +77,39 @@ pub fn get_branches_to_display(
Ok(branches_for_display)
}
///
#[derive(Debug, Default)]
pub struct BranchCompare {
///
pub ahead: usize,
///
pub behind: usize,
}
///
pub fn branch_compare_upstream(
repo_path: &str,
branch: &str,
) -> Result<BranchCompare> {
scope_time!("branch_compare_upstream");
let repo = utils::repo(repo_path)?;
let branch = repo.find_branch(branch, BranchType::Local)?;
let upstream = branch.upstream()?;
let branch_commit =
branch.into_reference().peel_to_commit()?.id();
let upstream_commit =
upstream.into_reference().peel_to_commit()?.id();
let (ahead, behind) =
repo.graph_ahead_behind(branch_commit, upstream_commit)?;
Ok(BranchCompare { ahead, behind })
}
/// Modify HEAD to point to a branch then checkout head, does not work if there are uncommitted changes
pub fn checkout_branch(
repo_path: &str,

View file

@ -20,8 +20,9 @@ pub mod utils;
pub(crate) use branch::get_branch_name;
pub use branch::{
checkout_branch, create_branch, delete_branch,
get_branches_to_display, rename_branch, BranchForDisplay,
branch_compare_upstream, checkout_branch, create_branch,
delete_branch, get_branches_to_display, rename_branch,
BranchCompare, BranchForDisplay,
};
pub use commit::{amend, commit, tag};
pub use commit_details::{

View file

@ -12,13 +12,17 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
sync::BranchCompare,
sync::{self, status::StatusType},
AsyncDiff, AsyncNotification, AsyncStatus, DiffParams, DiffType,
StatusParams, CWD,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use tui::layout::{Constraint, Direction, Layout};
use tui::{
layout::{Alignment, Constraint, Direction, Layout},
widgets::Paragraph,
};
///
#[derive(PartialEq)]
@ -45,6 +49,7 @@ pub struct Status {
git_diff: AsyncDiff,
git_status_workdir: AsyncStatus,
git_status_stage: AsyncStatus,
git_branch_state: BranchCompare,
queue: Queue,
git_action_executed: bool,
key_config: SharedKeyConfig,
@ -95,6 +100,7 @@ impl DrawableComponent for Status {
self.index_wd.draw(f, left_chunks[0])?;
self.index.draw(f, left_chunks[1])?;
self.diff.draw(f, chunks[1])?;
self.draw_branch_state(f, &left_chunks);
Ok(())
}
@ -141,10 +147,38 @@ impl Status {
git_status_workdir: AsyncStatus::new(sender.clone()),
git_status_stage: AsyncStatus::new(sender.clone()),
git_action_executed: false,
git_branch_state: BranchCompare::default(),
key_config,
}
}
fn draw_branch_state<B: tui::backend::Backend>(
&self,
f: &mut tui::Frame<B>,
chunks: &[tui::layout::Rect],
) {
let w = Paragraph::new(format!(
"\u{2191}{} \u{2193}{}",
self.git_branch_state.ahead, self.git_branch_state.behind
))
.alignment(Alignment::Right);
let mut rect = if self.index_wd.focused() {
let mut rect = chunks[0];
rect.y += rect.height.saturating_sub(1);
rect
} else {
chunks[1]
};
rect.x += 1;
rect.width = rect.width.saturating_sub(2);
rect.height =
rect.height.saturating_sub(rect.height.saturating_sub(1));
f.render_widget(w, rect);
}
fn can_focus_diff(&self) -> bool {
match self.focus {
Focus::WorkDir => self.index_wd.is_file_seleted(),
@ -216,6 +250,7 @@ impl Status {
.fetch(StatusParams::new(StatusType::Stage, true))?;
self.index_wd.update()?;
self.check_branch_state();
}
Ok(())
@ -356,6 +391,20 @@ impl Status {
}
}
}
fn check_branch_state(&mut self) {
self.git_branch_state = self.index_wd.branch_name().map_or(
BranchCompare::default(),
|branch| {
sync::branch_compare_upstream(CWD, branch.as_str())
.unwrap_or_default()
},
);
}
const fn can_push(&self) -> bool {
self.git_branch_state.ahead > 0
}
}
impl Component for Status {
@ -381,7 +430,7 @@ impl Component for Status {
out.push(CommandInfo::new(
strings::commands::status_push(&self.key_config),
self.index_wd.branch_name().is_some(),
self.can_push(),
true,
));
}