mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
fetch file diff based on hunks
This commit is contained in:
parent
63ec83f60d
commit
a60c3b2eb0
3 changed files with 141 additions and 28 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -21,4 +21,4 @@ jobs:
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --verbose
|
run: cargo build --verbose
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --workspace
|
run: cargo test --workspace -- --test-threads=1
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
//! sync git api for fetching a diff
|
//! sync git api for fetching a diff
|
||||||
|
|
||||||
use super::utils;
|
use super::utils;
|
||||||
use git2::{Delta, DiffDelta, DiffFormat, DiffOptions, Patch};
|
use git2::{
|
||||||
|
Delta, DiffDelta, DiffFormat, DiffHunk, DiffOptions, Patch,
|
||||||
|
};
|
||||||
use scopetime::scope_time;
|
use scopetime::scope_time;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
|
@ -33,9 +35,33 @@ pub struct DiffLine {
|
||||||
pub line_type: DiffLineType,
|
pub line_type: DiffLineType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
#[derive(Default, Clone, Copy, PartialEq)]
|
||||||
|
struct HunkHeader {
|
||||||
|
old_start: u32,
|
||||||
|
old_lines: u32,
|
||||||
|
new_start: u32,
|
||||||
|
new_lines: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DiffHunk<'_>> for HunkHeader {
|
||||||
|
fn from(h: DiffHunk) -> Self {
|
||||||
|
Self {
|
||||||
|
old_start: h.old_start(),
|
||||||
|
old_lines: h.old_lines(),
|
||||||
|
new_start: h.new_start(),
|
||||||
|
new_lines: h.new_lines(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
#[derive(Default, Clone, Hash)]
|
#[derive(Default, Clone, Hash)]
|
||||||
pub struct Diff(pub Vec<DiffLine>);
|
pub struct Hunk(pub Vec<DiffLine>);
|
||||||
|
|
||||||
|
///
|
||||||
|
#[derive(Default, Clone, Hash)]
|
||||||
|
pub struct Diff(pub Vec<Hunk>);
|
||||||
|
|
||||||
///
|
///
|
||||||
pub fn get_diff(p: String, stage: bool) -> Diff {
|
pub fn get_diff(p: String, stage: bool) -> Diff {
|
||||||
|
|
@ -64,13 +90,25 @@ pub fn get_diff(p: String, stage: bool) -> Diff {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res = Vec::new();
|
let mut res: Diff = Diff::default();
|
||||||
|
let mut current_lines = Vec::new();
|
||||||
|
let mut current_hunk: Option<HunkHeader> = None;
|
||||||
|
|
||||||
let mut put = |line: git2::DiffLine| {
|
let mut put = |hunk: Option<DiffHunk>, line: git2::DiffLine| {
|
||||||
let origin = line.origin();
|
if let Some(hunk) = hunk {
|
||||||
|
let hunk_header = HunkHeader::from(hunk);
|
||||||
|
|
||||||
if origin != 'F' {
|
match current_hunk {
|
||||||
let line_type = match origin {
|
None => current_hunk = Some(hunk_header),
|
||||||
|
Some(h) if h != hunk_header => {
|
||||||
|
res.0.push(Hunk(current_lines.clone()));
|
||||||
|
current_lines.clear();
|
||||||
|
current_hunk = Some(hunk_header)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
let line_type = match line.origin() {
|
||||||
'H' => DiffLineType::Header,
|
'H' => DiffLineType::Header,
|
||||||
'<' | '-' => DiffLineType::Delete,
|
'<' | '-' => DiffLineType::Delete,
|
||||||
'>' | '+' => DiffLineType::Add,
|
'>' | '+' => DiffLineType::Add,
|
||||||
|
|
@ -83,14 +121,7 @@ pub fn get_diff(p: String, stage: bool) -> Diff {
|
||||||
line_type,
|
line_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
if line_type == DiffLineType::Header && res.len() > 0 {
|
current_lines.push(diff_line);
|
||||||
res.push(DiffLine {
|
|
||||||
content: "\n".to_string(),
|
|
||||||
line_type: DiffLineType::None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.push(diff_line);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -113,8 +144,8 @@ pub fn get_diff(p: String, stage: bool) -> Diff {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
patch
|
patch
|
||||||
.print(&mut |_delta, _hunk, line: git2::DiffLine| {
|
.print(&mut |_delta, hunk:Option<DiffHunk>, line: git2::DiffLine| {
|
||||||
put(line);
|
put(hunk,line);
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -130,28 +161,35 @@ pub fn get_diff(p: String, stage: bool) -> Diff {
|
||||||
if !new_file_diff {
|
if !new_file_diff {
|
||||||
diff.print(
|
diff.print(
|
||||||
DiffFormat::Patch,
|
DiffFormat::Patch,
|
||||||
|_, _, line: git2::DiffLine| {
|
|_, hunk, line: git2::DiffLine| {
|
||||||
put(line);
|
put(hunk, line);
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Diff(res)
|
if !current_lines.is_empty() {
|
||||||
|
res.0.push(Hunk(current_lines))
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::sync::status::{get_index, StatusType};
|
use super::get_diff;
|
||||||
|
use crate::sync::{
|
||||||
|
stage_add,
|
||||||
|
status::{get_index, StatusType},
|
||||||
|
};
|
||||||
use git2::Repository;
|
use git2::Repository;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::Write,
|
io::Write,
|
||||||
|
path::Path,
|
||||||
};
|
};
|
||||||
// use std::path::Path;
|
|
||||||
use super::get_diff;
|
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
pub fn repo_init() -> (TempDir, Repository) {
|
pub fn repo_init() -> (TempDir, Repository) {
|
||||||
|
|
@ -201,7 +239,78 @@ mod tests {
|
||||||
|
|
||||||
let diff = get_diff("foo/bar.txt".to_string(), false);
|
let diff = get_diff("foo/bar.txt".to_string(), false);
|
||||||
|
|
||||||
assert_eq!(diff.0.len(), 4);
|
assert_eq!(diff.0.len(), 1);
|
||||||
assert_eq!(diff.0[1].content, "test\n");
|
assert_eq!(diff.0[0].0[1].content, "test\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static HUNK_A: &str = r"
|
||||||
|
1 start
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6 middle
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
0
|
||||||
|
1 end";
|
||||||
|
|
||||||
|
static HUNK_B: &str = r"
|
||||||
|
1 start
|
||||||
|
2 newa
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6 middle
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
0 newb
|
||||||
|
1 end";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hunks() {
|
||||||
|
let (_td, repo) = repo_init();
|
||||||
|
let root = repo.path().parent().unwrap();
|
||||||
|
|
||||||
|
//TODO: this makes the test not threading safe
|
||||||
|
assert!(env::set_current_dir(&root).is_ok());
|
||||||
|
|
||||||
|
let res = get_index(StatusType::WorkingDir);
|
||||||
|
assert_eq!(res.len(), 0);
|
||||||
|
|
||||||
|
let file_path = root.join("bar.txt");
|
||||||
|
|
||||||
|
{
|
||||||
|
File::create(&file_path)
|
||||||
|
.unwrap()
|
||||||
|
.write_all(HUNK_A.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = get_index(StatusType::WorkingDir);
|
||||||
|
assert_eq!(res.len(), 1);
|
||||||
|
assert_eq!(res[0].path, "bar.txt");
|
||||||
|
|
||||||
|
let res = stage_add(Path::new("bar.txt"));
|
||||||
|
assert_eq!(res, true);
|
||||||
|
assert_eq!(get_index(StatusType::Stage).len(), 1);
|
||||||
|
assert_eq!(get_index(StatusType::WorkingDir).len(), 0);
|
||||||
|
|
||||||
|
// overwrite with next content
|
||||||
|
{
|
||||||
|
File::create(&file_path)
|
||||||
|
.unwrap()
|
||||||
|
.write_all(HUNK_B.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(get_index(StatusType::Stage).len(), 1);
|
||||||
|
assert_eq!(get_index(StatusType::WorkingDir).len(), 1);
|
||||||
|
|
||||||
|
let res = get_diff("bar.txt".to_string(), false);
|
||||||
|
|
||||||
|
assert_eq!(res.0.len(), 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ use crate::{
|
||||||
components::{CommandInfo, Component},
|
components::{CommandInfo, Component},
|
||||||
strings,
|
strings,
|
||||||
};
|
};
|
||||||
use asyncgit::{hash, Diff, DiffLine, DiffLineType};
|
use asyncgit::{
|
||||||
|
hash, sync::diff::Hunk, Diff, DiffLine, DiffLineType,
|
||||||
|
};
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
|
|
@ -69,7 +71,9 @@ impl Component for DiffComponent {
|
||||||
.diff
|
.diff
|
||||||
.0
|
.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e: &DiffLine| {
|
.map(|h: &Hunk| h.0.clone())
|
||||||
|
.flatten()
|
||||||
|
.map(|e: DiffLine| {
|
||||||
let content = e.content.clone();
|
let content = e.content.clone();
|
||||||
match e.line_type {
|
match e.line_type {
|
||||||
DiffLineType::Delete => Text::Styled(
|
DiffLineType::Delete => Text::Styled(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue