more clean solution that does not need git cli

* make test run in parallel again
* better test structure
This commit is contained in:
Stephan Dilly 2020-03-28 09:39:16 +01:00
parent 96b739c977
commit 41a52cfc8c
13 changed files with 168 additions and 192 deletions

View File

@ -19,8 +19,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
run: cargo build
- name: Run tests
run: cargo test --workspace -- --test-threads=1
run: cargo test --workspace
- name: Run clippy
run: cargo clean && cargo clippy --all-features

View File

@ -3,7 +3,7 @@ debug:
GITUI_LOGGING=true cargo run --features=timing
test:
cargo test --workspace -- --test-threads=1
cargo test --workspace
clippy:
cargo clean

View File

@ -66,6 +66,3 @@ GITUI_LOGGING=true gitui
* https://github.com/jesseduffield/lazygit
* https://github.com/jonas/tig
* https://github.com/git-up/GitUp (would be nice to comeup with a way to have the map view available in a terminal tool)
asdad

View File

@ -1,4 +1,4 @@
use crate::{hash, sync, AsyncNotification, Diff};
use crate::{hash, sync, AsyncNotification, Diff, CWD};
use crossbeam_channel::Sender;
use log::trace;
use std::{
@ -76,7 +76,7 @@ impl AsyncDiff {
let sender = self.sender.clone();
rayon_core::spawn(move || {
let res =
sync::diff::get_diff(params.0.clone(), params.1);
sync::diff::get_diff(CWD, params.0.clone(), params.1);
let mut notify = false;
{
let mut current = arc_current.lock().unwrap();

View File

@ -31,6 +31,9 @@ pub enum AsyncNotification {
Diff,
}
///
pub static CWD: &str = "./";
/// helper function to calculate the hash of an arbitrary type that implements the `Hash` trait
pub fn hash<T: Hash + ?Sized>(v: &T) -> u64 {
let mut hasher = DefaultHasher::new();

View File

@ -1,4 +1,4 @@
use crate::{hash, sync, AsyncNotification, StatusItem};
use crate::{hash, sync, AsyncNotification, StatusItem, CWD};
use crossbeam_channel::Sender;
use log::trace;
use std::{
@ -85,8 +85,8 @@ impl AsyncStatus {
fn get_status() -> Status {
let work_dir =
sync::status::get_index(StatusType::WorkingDir);
let stage = sync::status::get_index(StatusType::Stage);
sync::status::get_status(CWD, StatusType::WorkingDir);
let stage = sync::status::get_status(CWD, StatusType::Stage);
Status { stage, work_dir }
}

View File

@ -5,7 +5,7 @@ use git2::{
Delta, DiffDelta, DiffFormat, DiffHunk, DiffOptions, Patch,
};
use scopetime::scope_time;
use std::fs;
use std::{fs, path::Path};
///
#[derive(Copy, Clone, PartialEq, Hash)]
@ -64,10 +64,10 @@ pub struct Hunk(pub Vec<DiffLine>);
pub struct Diff(pub Vec<Hunk>, pub u16);
///
pub fn get_diff(p: String, stage: bool) -> Diff {
pub fn get_diff(repo_path: &str, p: String, stage: bool) -> Diff {
scope_time!("get_diff");
let repo = utils::repo();
let repo = utils::repo(repo_path);
let mut opt = DiffOptions::new();
opt.pathspec(p);
@ -134,16 +134,18 @@ pub fn get_diff(p: String, stage: bool) -> Diff {
let delta: DiffDelta = diff.deltas().next().unwrap();
if delta.status() == Delta::Untracked {
let newfile_path = delta.new_file().path().unwrap();
let repo_path = Path::new(repo_path);
let newfile_path =
repo_path.join(delta.new_file().path().unwrap());
let newfile_content =
fs::read_to_string(newfile_path).unwrap();
fs::read_to_string(&newfile_path).unwrap();
let mut patch = Patch::from_buffers(
&[],
None,
newfile_content.as_bytes(),
Some(newfile_path),
Some(&newfile_path),
Some(&mut opt),
)
.unwrap();
@ -186,51 +188,22 @@ mod tests {
use super::get_diff;
use crate::sync::{
stage_add,
status::{get_index, StatusType},
status::{get_status, StatusType},
tests::repo_init,
};
use git2::Repository;
use std::env;
use std::{
fs::{self, File},
io::Write,
path::Path,
};
use tempfile::TempDir;
pub fn repo_init() -> (TempDir, Repository) {
let td = TempDir::new().unwrap();
let repo = Repository::init(td.path()).unwrap();
{
let mut config = repo.config().unwrap();
config.set_str("user.name", "name").unwrap();
config.set_str("user.email", "email").unwrap();
let mut index = repo.index().unwrap();
let id = index.write_tree().unwrap();
let tree = repo.find_tree(id).unwrap();
let sig = repo.signature().unwrap();
repo.commit(
Some("HEAD"),
&sig,
&sig,
"initial",
&tree,
&[],
)
.unwrap();
}
(td, repo)
}
#[test]
fn test_untracked_subfolder() {
let (_td, repo) = repo_init();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
assert!(env::set_current_dir(&root).is_ok());
let res = get_index(StatusType::WorkingDir);
let res = get_status(repo_path, StatusType::WorkingDir);
assert_eq!(res.len(), 0);
fs::create_dir(&root.join("foo")).unwrap();
@ -239,10 +212,11 @@ mod tests {
.write_all(b"test\nfoo")
.unwrap();
let res = get_index(StatusType::WorkingDir);
let res = get_status(repo_path, StatusType::WorkingDir);
assert_eq!(res.len(), 1);
let diff = get_diff("foo/bar.txt".to_string(), false);
let diff =
get_diff(repo_path, "foo/bar.txt".to_string(), false);
assert_eq!(diff.0.len(), 1);
assert_eq!(diff.0[0].0[1].content, "test\n");
@ -278,11 +252,9 @@ mod tests {
fn test_hunks() {
let (_td, repo) = repo_init();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
//TODO: this makes the test not threading safe
assert!(env::set_current_dir(&root).is_ok());
let res = get_index(StatusType::WorkingDir);
let res = get_status(repo_path, StatusType::WorkingDir);
assert_eq!(res.len(), 0);
let file_path = root.join("bar.txt");
@ -294,14 +266,17 @@ mod tests {
.unwrap();
}
let res = get_index(StatusType::WorkingDir);
let res = get_status(repo_path, StatusType::WorkingDir);
assert_eq!(res.len(), 1);
assert_eq!(res[0].path, "bar.txt");
let res = stage_add(Path::new("bar.txt"));
let res = stage_add(repo_path, Path::new("bar.txt"));
assert_eq!(res, true);
assert_eq!(get_index(StatusType::Stage).len(), 1);
assert_eq!(get_index(StatusType::WorkingDir).len(), 0);
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
assert_eq!(
get_status(repo_path, StatusType::WorkingDir).len(),
0
);
// overwrite with next content
{
@ -311,10 +286,13 @@ mod tests {
.unwrap();
}
assert_eq!(get_index(StatusType::Stage).len(), 1);
assert_eq!(get_index(StatusType::WorkingDir).len(), 1);
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
assert_eq!(
get_status(repo_path, StatusType::WorkingDir).len(),
1
);
let res = get_diff("bar.txt".to_string(), false);
let res = get_diff(repo_path, "bar.txt".to_string(), false);
assert_eq!(res.0.len(), 2)
}

View File

@ -5,5 +5,75 @@ mod reset;
pub mod status;
pub mod utils;
pub use reset::{index_reset, stage_reset};
pub use reset::{reset_stage, reset_workdir};
pub use utils::{commit, stage_add};
#[cfg(test)]
mod tests {
use git2::Repository;
use std::process::Command;
use tempfile::TempDir;
pub fn repo_init() -> (TempDir, Repository) {
let td = TempDir::new().unwrap();
let repo = Repository::init(td.path()).unwrap();
{
let mut config = repo.config().unwrap();
config.set_str("user.name", "name").unwrap();
config.set_str("user.email", "email").unwrap();
let mut index = repo.index().unwrap();
let id = index.write_tree().unwrap();
let tree = repo.find_tree(id).unwrap();
let sig = repo.signature().unwrap();
repo.commit(
Some("HEAD"),
&sig,
&sig,
"initial",
&tree,
&[],
)
.unwrap();
}
(td, repo)
}
///
pub fn debug_cmd_print(path: &str, cmd: &str) {
eprintln!("\n----\n{}", debug_cmd(path, cmd))
}
fn debug_cmd(path: &str, cmd: &str) -> String {
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(&["/C", cmd])
.current_dir(path)
.output()
} else {
Command::new("sh")
.arg("-c")
.arg(cmd)
.current_dir(path)
.output()
};
let output = output.unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();
format!(
"{}{}",
if stdout.is_empty() {
String::new()
} else {
format!("out:\n{}", stdout)
},
if stderr.is_empty() {
String::new()
} else {
format!("err:\n{}", stderr)
}
)
}
}

View File

@ -1,18 +1,13 @@
use super::utils::repo_at;
use git2::ObjectType;
use super::utils::repo;
use git2::{build::CheckoutBuilder, ObjectType};
use scopetime::scope_time;
use std::{path::Path, process::Command};
use std::path::Path;
///
pub fn stage_reset(path: &Path) -> bool {
stage_reset_at("./", path)
}
pub fn reset_stage(repo_path: &str, path: &Path) -> bool {
scope_time!("reset_stage");
///
pub fn stage_reset_at(repo_path: &str, path: &Path) -> bool {
scope_time!("stage_reset_at");
let repo = repo_at(repo_path);
let repo = repo(repo_path);
let reference = repo.head().unwrap();
let obj = repo
@ -30,94 +25,41 @@ pub fn stage_reset_at(repo_path: &str, path: &Path) -> bool {
}
///
pub fn index_reset(path: &Path) -> bool {
index_reset_at("./", path)
}
pub fn reset_workdir(repo_path: &str, path: &Path) -> bool {
scope_time!("reset_workdir");
///
pub fn index_reset_at(repo_path: &str, path: &Path) -> bool {
let cmd = format!("git checkout {}", path.to_str().unwrap());
let repo = repo(repo_path);
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(&["/C", cmd.as_str()])
.current_dir(repo_path)
.output()
} else {
Command::new("sh")
.arg("-c")
.arg(cmd.as_str())
.current_dir(repo_path)
.output()
};
let mut checkout_opts = CheckoutBuilder::new();
checkout_opts
.remove_untracked(true)
.force()
.update_index(false)
.path(&path);
if let Ok(out) = output {
// dbg!(String::from_utf8(out.stderr.clone()).unwrap());
String::from_utf8(out.stderr).unwrap()
== "Updated 1 path from the index\n"
} else {
false
}
//first reset working dir file
repo.checkout_head(Some(&mut checkout_opts)).unwrap();
//------------------------------------
//TODO: why is this broken with libgit2 ???
//------------------------------------
let mut checkout_opts = CheckoutBuilder::new();
checkout_opts
.update_index(true) // windows: needs this to be true WTF?!
.path(&path);
// scope_time!("index_reset");
// now reset staged changes back to working dir
repo.checkout_index(None, Some(&mut checkout_opts)).unwrap();
// let repo = repo_at(repo_path);
// let mut checkout_opts = CheckoutBuilder::new();
// checkout_opts
// .remove_untracked(true)
// .force()
// .update_index(false)
// .allow_conflicts(true)
// .path(&path);
// if repo.checkout_head(Some(&mut checkout_opts)).is_ok() {
// return true;
// }
// false
true
}
#[cfg(test)]
mod tests {
use super::index_reset_at;
use super::reset_workdir;
use crate::sync::{
status::{get_index_at, StatusType},
utils::stage_add_at,
status::{get_status, StatusType},
tests::{debug_cmd_print, repo_init},
utils::stage_add,
};
use git2::Repository;
use std::{fs::File, io::Write, path::Path};
use tempfile::TempDir;
pub fn repo_init() -> (TempDir, Repository) {
let td = TempDir::new().unwrap();
let repo = Repository::init(td.path()).unwrap();
{
let mut config = repo.config().unwrap();
config.set_str("user.name", "name").unwrap();
config.set_str("user.email", "email").unwrap();
let mut index = repo.index().unwrap();
let id = index.write_tree().unwrap();
let tree = repo.find_tree(id).unwrap();
let sig = repo.signature().unwrap();
repo.commit(
Some("HEAD"),
&sig,
&sig,
"initial",
&tree,
&[],
)
.unwrap();
}
(td, repo)
}
static HUNK_A: &str = r"
1 start
@ -151,7 +93,7 @@ mod tests {
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let res = get_index_at(repo_path, StatusType::WorkingDir);
let res = get_status(repo_path, StatusType::WorkingDir);
assert_eq!(res.len(), 0);
let file_path = root.join("bar.txt");
@ -163,7 +105,11 @@ mod tests {
.unwrap();
}
stage_add_at(repo_path, Path::new("bar.txt"));
debug_cmd_print(repo_path, "git status");
stage_add(repo_path, Path::new("bar.txt"));
debug_cmd_print(repo_path, "git status");
// overwrite with next content
{
@ -173,24 +119,22 @@ mod tests {
.unwrap();
}
debug_cmd_print(repo_path, "git status");
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
assert_eq!(
get_index_at(repo_path, StatusType::Stage).len(),
1
);
assert_eq!(
get_index_at(repo_path, StatusType::WorkingDir).len(),
get_status(repo_path, StatusType::WorkingDir).len(),
1
);
let res = index_reset_at(repo_path, Path::new("bar.txt"));
let res = reset_workdir(repo_path, Path::new("bar.txt"));
assert_eq!(res, true);
debug_cmd_print(repo_path, "git status");
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
assert_eq!(
get_index_at(repo_path, StatusType::Stage).len(),
1
);
assert_eq!(
get_index_at(repo_path, StatusType::WorkingDir).len(),
get_status(repo_path, StatusType::WorkingDir).len(),
0
);
}

View File

@ -63,18 +63,13 @@ impl Into<StatusShow> for StatusType {
}
///
pub fn get_index(status_type: StatusType) -> Vec<StatusItem> {
get_index_at("./", status_type)
}
///
pub fn get_index_at(
pub fn get_status(
repo_path: &str,
status_type: StatusType,
) -> Vec<StatusItem> {
scope_time!("get_index");
let repo = utils::repo_at(repo_path);
let repo = utils::repo(repo_path);
let statuses = repo
.statuses(Some(

View File

@ -4,14 +4,8 @@ use git2::{IndexAddOption, Repository, RepositoryOpenFlags};
use scopetime::scope_time;
use std::path::Path;
//TODO: get rid of this
///
pub fn repo() -> Repository {
repo_at("./")
}
///
pub fn repo_at(repo_path: &str) -> Repository {
pub fn repo(repo_path: &str) -> Repository {
let repo = Repository::open_ext(
repo_path,
RepositoryOpenFlags::empty(),
@ -27,10 +21,10 @@ pub fn repo_at(repo_path: &str) -> Repository {
}
///
pub fn commit(msg: &str) {
pub fn commit(repo_path: &str, msg: &str) {
scope_time!("commit");
let repo = repo();
let repo = repo(repo_path);
let signature = repo.signature().unwrap();
let reference = repo.head().unwrap();
@ -52,15 +46,10 @@ pub fn commit(msg: &str) {
}
///
pub fn stage_add(path: &Path) -> bool {
stage_add_at("./", path)
}
///
pub fn stage_add_at(repo_path: &str, path: &Path) -> bool {
pub fn stage_add(repo_path: &str, path: &Path) -> bool {
scope_time!("stage_add");
let repo = repo_at(repo_path);
let repo = repo(repo_path);
let mut index = repo.index().unwrap();

View File

@ -7,7 +7,7 @@ use crate::{
};
use asyncgit::{
current_tick, sync, AsyncDiff, AsyncNotification, AsyncStatus,
DiffParams,
DiffParams, CWD,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -431,14 +431,14 @@ impl App {
if let Some(i) = self.index_wd.selection() {
let path = Path::new(i.path.as_str());
if sync::stage_add(path) {
if sync::stage_add(CWD, path) {
self.update();
}
}
} else if let Some(i) = self.index.selection() {
let path = Path::new(i.path.as_str());
if sync::stage_reset(path) {
if sync::reset_stage(CWD, path) {
self.update();
}
}
@ -449,7 +449,7 @@ impl App {
if let Some(i) = self.index_wd.selection() {
let path = Path::new(i.path.as_str());
if sync::index_reset(path) {
if sync::reset_workdir(CWD, path) {
self.update();
}
}

View File

@ -1,6 +1,6 @@
use super::{CommandInfo, Component};
use crate::{strings, ui};
use asyncgit::sync;
use asyncgit::{sync, CWD};
use crossterm::event::{Event, KeyCode};
use std::borrow::Cow;
use tui::{
@ -99,7 +99,7 @@ impl Component for CommitComponent {
impl CommitComponent {
fn commit(&mut self) {
sync::commit(&self.msg);
sync::commit(CWD, &self.msg);
self.msg.clear();
self.hide();