Skip to content

Log view #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
May 5, 2020
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dirs = "2.0"
crossbeam-channel = "0.4"
scopeguard = "1.1"
bitflags = "1.2"
chrono = "0.4"
backtrace = { version = "0.3" }
scopetime = { path = "./scopetime", version = "0.1" }
asyncgit = { path = "./asyncgit", version = "0.2" }
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ install:
cargo install --path "."

install-debug:
cargo install --features=timing --path "."
cargo install --features=timing --path "." --offline
4 changes: 4 additions & 0 deletions asyncgit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
#![deny(clippy::all)]

mod diff;
mod revlog;
mod status;
pub mod sync;

pub use crate::{
diff::{AsyncDiff, DiffParams},
revlog::AsyncLog,
status::AsyncStatus,
sync::{
diff::{DiffLine, DiffLineType, FileDiff},
Expand All @@ -30,6 +32,8 @@ pub enum AsyncNotification {
Status,
///
Diff,
///
Log,
}

/// current working director `./`
Expand Down
105 changes: 105 additions & 0 deletions asyncgit/src/revlog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use crate::{sync, AsyncNotification, CWD};
use crossbeam_channel::Sender;
use git2::Oid;
use scopetime::scope_time;
use std::{
iter::FromIterator,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
};
use sync::{utils::repo, LogWalker};

///
pub struct AsyncLog {
current: Arc<Mutex<Vec<Oid>>>,
sender: Sender<AsyncNotification>,
pending: Arc<AtomicBool>,
}

static LIMIT_COUNT: usize = 1000;

impl AsyncLog {
///
pub fn new(sender: Sender<AsyncNotification>) -> Self {
Self {
current: Arc::new(Mutex::new(Vec::new())),
sender,
pending: Arc::new(AtomicBool::new(false)),
}
}

///
pub fn count(&mut self) -> usize {
self.current.lock().unwrap().len()
}

///
pub fn get_slice(
&self,
start_index: usize,
amount: usize,
) -> Vec<Oid> {
let list = self.current.lock().unwrap();
let list_len = list.len();
let min = start_index.min(list_len);
let max = min + amount;
let max = max.min(list_len);
Vec::from_iter(list[min..max].iter().cloned())
}

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

///
pub fn fetch(&mut self) {
if !self.is_pending() {
self.clear();

let arc_current = Arc::clone(&self.current);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
rayon_core::spawn(move || {
arc_pending.store(true, Ordering::Relaxed);

scope_time!("async::revlog");

let mut entries = Vec::with_capacity(LIMIT_COUNT);
let r = repo(CWD);
let mut walker = LogWalker::new(&r);
loop {
entries.clear();
let res_is_err = walker
.read(&mut entries, LIMIT_COUNT)
.is_err();

if !res_is_err {
let mut current = arc_current.lock().unwrap();
current.extend(entries.iter());
}

if res_is_err || entries.len() <= 1 {
break;
} else {
Self::notify(&sender);
}
}

arc_pending.store(false, Ordering::Relaxed);

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

fn clear(&mut self) {
self.current.lock().unwrap().clear();
}

fn notify(sender: &Sender<AsyncNotification>) {
sender.send(AsyncNotification::Log).expect("error sending");
}
}
101 changes: 101 additions & 0 deletions asyncgit/src/sync/commits_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use super::utils::repo;
use git2::{Commit, Error, Oid};
use scopetime::scope_time;

///
#[derive(Debug)]
pub struct CommitInfo {
///
pub message: String,
///
pub time: i64,
///
pub author: String,
///
pub hash: String,
}

///
pub fn get_commits_info(
repo_path: &str,
ids: &[Oid],
) -> Result<Vec<CommitInfo>, Error> {
scope_time!("get_commits_info");

let repo = repo(repo_path);

let commits = ids.iter().map(|id| repo.find_commit(*id).unwrap());

let res = commits
.map(|c: Commit| {
let message = get_message(&c);
let author = if let Some(name) = c.author().name() {
String::from(name)
} else {
String::from("<unknown>")
};
CommitInfo {
message,
author,
time: c.time().seconds(),
hash: c.id().to_string(),
}
})
.collect::<Vec<_>>();

Ok(res)
}

fn get_message(c: &Commit) -> String {
if let Some(msg) = c.message() {
limit_str(msg, 50)
} else {
String::from("<unknown>")
}
}

fn limit_str(s: &str, limit: usize) -> String {
if let Some(first) = s.lines().next() {
first.chars().take(limit).collect::<String>()
} else {
String::new()
}
}

#[cfg(test)]
mod tests {

use super::get_commits_info;
use crate::sync::{
commit, stage_add_file, tests::repo_init_empty,
};
use std::{
fs::File,
io::{Error, Write},
path::Path,
};

#[test]
fn test_log() -> Result<(), Error> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();

File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path);
let c1 = commit(repo_path, "commit1");
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path);
let c2 = commit(repo_path, "commit2");

let res = get_commits_info(repo_path, &vec![c2, c1]).unwrap();

assert_eq!(res.len(), 2);
assert_eq!(res[0].message.as_str(), "commit2");
assert_eq!(res[0].author.as_str(), "name");
assert_eq!(res[1].message.as_str(), "commit1");

Ok(())
}
}
117 changes: 117 additions & 0 deletions asyncgit/src/sync/logwalker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use git2::{Error, Oid, Repository, Revwalk};

///
pub struct LogWalker<'a> {
repo: &'a Repository,
revwalk: Option<Revwalk<'a>>,
}

impl<'a> LogWalker<'a> {
///
pub fn new(repo: &'a Repository) -> Self {
Self {
repo,
revwalk: None,
}
}

///
pub fn read(
&mut self,
out: &mut Vec<Oid>,
limit: usize,
) -> Result<usize, Error> {
let mut count = 0_usize;

if self.revwalk.is_none() {
let mut walk = self.repo.revwalk()?;
walk.push_head()?;
self.revwalk = Some(walk);
}

if let Some(ref mut walk) = self.revwalk {
for id in walk {
if let Ok(id) = id {
out.push(id);
count += 1;

if count == limit {
break;
}
}
}
}

Ok(count)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::sync::{
commit, get_commits_info, stage_add_file,
tests::repo_init_empty,
};
use std::{
fs::File,
io::{Error, Write},
path::Path,
};

#[test]
fn test_limit() -> Result<(), Error> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();

File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path);
commit(repo_path, "commit1");
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path);
let oid2 = commit(repo_path, "commit2");

let mut items = Vec::new();
let mut walk = LogWalker::new(&repo);
walk.read(&mut items, 1).unwrap();

assert_eq!(items.len(), 1);
assert_eq!(items[0], oid2);

Ok(())
}

#[test]
fn test_logwalker() -> Result<(), Error> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();

File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path);
commit(repo_path, "commit1");
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path);
let oid2 = commit(repo_path, "commit2");

let mut items = Vec::new();
let mut walk = LogWalker::new(&repo);
walk.read(&mut items, 100).unwrap();

let info = get_commits_info(repo_path, &items).unwrap();
dbg!(&info);

assert_eq!(items.len(), 2);
assert_eq!(items[0], oid2);

let mut items = Vec::new();
walk.read(&mut items, 100).unwrap();

assert_eq!(items.len(), 0);

Ok(())
}
}
4 changes: 4 additions & 0 deletions asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
//! sync git api

mod commits_info;
pub mod diff;
mod hooks;
mod hunks;
mod logwalker;
mod reset;
pub mod status;
pub mod utils;

pub use commits_info::{get_commits_info, CommitInfo};
pub use hooks::{hooks_commit_msg, hooks_post_commit, HookResult};
pub use hunks::{stage_hunk, unstage_hunk};
pub use logwalker::LogWalker;
pub use reset::{
reset_stage, reset_workdir_file, reset_workdir_folder,
};
Expand Down
Loading