Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- async fetching tags to improve reactivity in giant repos ([#170](https://github.com/extrawurst/gitui/issues/170))

### Fixed
- removed unmaintained dependency `spin` ([#172](https://github.com/extrawurst/gitui/issues/172))
- opening relative paths in external editor may fail in subpaths ([#184](https://github.com/extrawurst/gitui/issues/184))
- crashes in revlog with utf8 commit messages ([#188](https://github.com/extrawurst/gitui/issues/188))
- `add_to_ignore` failed on files without a newline at EOF ([#191](https://github.com/extrawurst/gitui/issues/191))
- new tags were not picked up in revlog view ([#190](https://github.com/extrawurst/gitui/issues/190))

## [0.8.1] - 2020-07-07

Expand Down
12 changes: 7 additions & 5 deletions asyncgit/src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,13 @@ impl AsyncDiff {

arc_pending.fetch_sub(1, Ordering::Relaxed);

if notify {
sender
.send(AsyncNotification::Diff)
.expect("error sending diff");
}
sender
.send(if notify {
AsyncNotification::Diff
} else {
AsyncNotification::FinishUnchanged
})
.expect("error sending diff");
});

Ok(None)
Expand Down
6 changes: 6 additions & 0 deletions asyncgit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod error;
mod revlog;
mod status;
pub mod sync;
mod tags;

pub use crate::{
commit_files::AsyncCommitFiles,
Expand All @@ -23,6 +24,7 @@ pub use crate::{
diff::{DiffLine, DiffLineType, FileDiff},
status::{StatusItem, StatusItemType},
},
tags::AsyncTags,
};
use std::{
collections::hash_map::DefaultHasher,
Expand All @@ -32,6 +34,8 @@ use std::{
/// this type is used to communicate events back through the channel
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AsyncNotification {
/// this indicates that no new state was fetched but that a async process finished
FinishUnchanged,
///
Status,
///
Expand All @@ -40,6 +44,8 @@ pub enum AsyncNotification {
Log,
///
CommitFiles,
///
Tags,
}

/// current working director `./`
Expand Down
1 change: 1 addition & 0 deletions asyncgit/src/revlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ impl AsyncLog {
)
.expect("failed to fetch");
arc_pending.store(false, Ordering::Relaxed);

Self::notify(&sender);
});

Expand Down
2 changes: 1 addition & 1 deletion asyncgit/src/sync/commits_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use git2::{Commit, Error, Oid};
use scopetime::scope_time;

/// identifies a single commit
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct CommitId(Oid);

impl CommitId {
Expand Down
1 change: 1 addition & 0 deletions asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod tags;
pub mod utils;

pub(crate) use branch::get_branch_name;

pub use commit::{amend, commit};
pub use commit_details::{get_commit_details, CommitDetails};
pub use commit_files::get_commit_files;
Expand Down
4 changes: 2 additions & 2 deletions asyncgit/src/sync/tags.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::{utils::repo, CommitId};
use crate::error::Result;
use scopetime::scope_time;
use std::collections::HashMap;
use std::collections::BTreeMap;

/// hashmap of tag target commit hash to tag names
pub type Tags = HashMap<CommitId, Vec<String>>;
pub type Tags = BTreeMap<CommitId, Vec<String>>;

/// returns `Tags` type filled with all tags found in repo
pub fn get_tags(repo_path: &str) -> Result<Tags> {
Expand Down
127 changes: 127 additions & 0 deletions asyncgit/src/tags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use crate::{
error::Result,
hash,
sync::{self},
AsyncNotification, CWD,
};
use crossbeam_channel::Sender;
use std::{
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
time::{Duration, Instant},
};
use sync::Tags;

///
#[derive(Default, Clone)]
struct TagsResult {
hash: u64,
tags: Tags,
}

///
pub struct AsyncTags {
last: Arc<Mutex<Option<(Instant, TagsResult)>>>,
sender: Sender<AsyncNotification>,
pending: Arc<AtomicUsize>,
}

impl AsyncTags {
///
pub fn new(sender: &Sender<AsyncNotification>) -> Self {
Self {
last: Arc::new(Mutex::new(None)),
sender: sender.clone(),
pending: Arc::new(AtomicUsize::new(0)),
}
}

/// last fetched result
pub fn last(&mut self) -> Result<Option<Tags>> {
let last = self.last.lock()?;

Ok(last.clone().map(|last| last.1.tags))
}

///
pub fn is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed) > 0
}

fn is_outdated(&self, dur: Duration) -> Result<bool> {
let last = self.last.lock()?;

Ok(last
.as_ref()
.map(|(last_time, _)| last_time.elapsed() > dur)
.unwrap_or(true))
}

///
pub fn request(
&mut self,
dur: Duration,
force: bool,
) -> Result<()> {
log::trace!("request");

if !force && (self.is_pending() || !self.is_outdated(dur)?) {
return Ok(());
}

let arc_last = Arc::clone(&self.last);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
rayon_core::spawn(move || {
arc_pending.fetch_add(1, Ordering::Relaxed);

let notify = AsyncTags::getter(arc_last)
.expect("error getting tags");

arc_pending.fetch_sub(1, Ordering::Relaxed);

sender
.send(if notify {
AsyncNotification::Tags
} else {
AsyncNotification::FinishUnchanged
})
.expect("error sending notify");
});

Ok(())
}

fn getter(
arc_last: Arc<Mutex<Option<(Instant, TagsResult)>>>,
) -> Result<bool> {
let tags = sync::get_tags(CWD)?;

let hash = hash(&tags);

if Self::last_hash(arc_last.clone())
.map(|last| last == hash)
.unwrap_or_default()
{
return Ok(false);
}

{
let mut last = arc_last.lock()?;
let now = Instant::now();
*last = Some((now, TagsResult { tags, hash }));
}

Ok(true)
}

fn last_hash(
last: Arc<Mutex<Option<(Instant, TagsResult)>>>,
) -> Option<u64> {
last.lock()
.ok()
.and_then(|last| last.as_ref().map(|(_, last)| last.hash))
}
}
8 changes: 5 additions & 3 deletions src/components/commit_details/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl DetailsComponent {
pub fn set_commit(
&mut self,
id: Option<CommitId>,
tags: &Tags,
tags: Option<&Tags>,
) -> Result<()> {
self.tags.clear();

Expand All @@ -51,8 +51,10 @@ impl DetailsComponent {
};

if let Some(id) = id {
if let Some(tags) = tags.get(&id) {
self.tags.extend(tags.clone());
if let Some(tags) = tags {
if let Some(tags) = tags.get(&id) {
self.tags.extend(tags.clone());
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/commit_details/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl CommitDetailsComponent {
pub fn set_commit(
&mut self,
id: Option<CommitId>,
tags: &Tags,
tags: Option<&Tags>,
) -> Result<()> {
self.details.set_commit(id, tags)?;

Expand Down
6 changes: 0 additions & 6 deletions src/components/commitlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,8 @@ impl CommitList {
self.tags.as_ref()
}

///
pub fn has_tags(&self) -> bool {
self.tags.is_some()
}

///
pub fn clear(&mut self) {
self.tags = None;
self.items.clear();
}

Expand Down
6 changes: 3 additions & 3 deletions src/components/inspect_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
sync::{CommitId, Tags},
AsyncDiff, AsyncNotification, DiffParams, DiffType,
sync::CommitId, AsyncDiff, AsyncNotification, DiffParams,
DiffType,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
Expand Down Expand Up @@ -225,7 +225,7 @@ impl InspectCommitComponent {
}

fn update(&mut self) -> Result<()> {
self.details.set_commit(self.commit_id, &Tags::new())?;
self.details.set_commit(self.commit_id, None)?;
self.update_diff()?;

Ok(())
Expand Down
18 changes: 13 additions & 5 deletions src/tabs/revlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ use anyhow::Result;
use asyncgit::{
cached,
sync::{self, CommitId},
AsyncLog, AsyncNotification, FetchStatus, CWD,
AsyncLog, AsyncNotification, AsyncTags, FetchStatus, CWD,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use std::time::Duration;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
Expand All @@ -30,6 +31,7 @@ pub struct Revlog {
commit_details: CommitDetailsComponent,
list: CommitList,
git_log: AsyncLog,
git_tags: AsyncTags,
queue: Queue,
visible: bool,
branch_name: cached::BranchName,
Expand All @@ -51,6 +53,7 @@ impl Revlog {
),
list: CommitList::new(strings::LOG_TITLE, theme),
git_log: AsyncLog::new(sender),
git_tags: AsyncTags::new(sender),
visible: false,
branch_name: cached::BranchName::new(CWD),
}
Expand All @@ -59,6 +62,7 @@ impl Revlog {
///
pub fn any_work_pending(&self) -> bool {
self.git_log.is_pending()
|| self.git_tags.is_pending()
|| self.commit_details.any_work_pending()
}

Expand All @@ -78,9 +82,7 @@ impl Revlog {
self.fetch_commits()?;
}

if !self.list.has_tags() || log_changed {
self.list.set_tags(sync::get_tags(CWD)?);
}
self.git_tags.request(Duration::from_secs(3), false)?;

self.list.set_branch(
self.branch_name.lookup().map(Some).unwrap_or(None),
Expand All @@ -89,7 +91,7 @@ impl Revlog {
if self.commit_details.is_visible() {
self.commit_details.set_commit(
self.selected_commit(),
self.list.tags().expect("tags"),
self.list.tags(),
)?;
}
}
Expand All @@ -106,6 +108,12 @@ impl Revlog {
match ev {
AsyncNotification::CommitFiles
| AsyncNotification::Log => self.update()?,
AsyncNotification::Tags => {
if let Some(tags) = self.git_tags.last()? {
self.list.set_tags(tags);
self.update()?;
}
}
_ => (),
}
}
Expand Down