mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
178 lines
4 KiB
Rust
178 lines
4 KiB
Rust
use super::{
|
|
diff::{get_diff_raw, HunkHeader},
|
|
utils::repo,
|
|
};
|
|
use crate::{
|
|
error::{Error, Result},
|
|
hash,
|
|
};
|
|
use git2::{ApplyLocation, ApplyOptions, Diff};
|
|
use scopetime::scope_time;
|
|
|
|
///
|
|
pub fn stage_hunk(
|
|
repo_path: &str,
|
|
file_path: String,
|
|
hunk_hash: u64,
|
|
) -> Result<()> {
|
|
scope_time!("stage_hunk");
|
|
|
|
let repo = repo(repo_path)?;
|
|
|
|
let diff = get_diff_raw(&repo, &file_path, false, false)?;
|
|
|
|
let mut opt = ApplyOptions::new();
|
|
opt.hunk_callback(|hunk| {
|
|
let header = HunkHeader::from(hunk.unwrap());
|
|
hash(&header) == hunk_hash
|
|
});
|
|
|
|
repo.apply(&diff, ApplyLocation::Index, Some(&mut opt))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// this will fail for an all untracked file
|
|
pub fn reset_hunk(
|
|
repo_path: &str,
|
|
file_path: String,
|
|
hunk_hash: u64,
|
|
) -> Result<()> {
|
|
scope_time!("reset_hunk");
|
|
|
|
let repo = repo(repo_path)?;
|
|
|
|
let diff = get_diff_raw(&repo, &file_path, false, false)?;
|
|
|
|
let hunk_index = find_hunk_index(&diff, hunk_hash);
|
|
if let Some(hunk_index) = hunk_index {
|
|
let mut hunk_idx = 0;
|
|
let mut opt = ApplyOptions::new();
|
|
opt.hunk_callback(|_hunk| {
|
|
let res = hunk_idx == hunk_index;
|
|
hunk_idx += 1;
|
|
res
|
|
});
|
|
|
|
let diff = get_diff_raw(&repo, &file_path, false, true)?;
|
|
|
|
repo.apply(&diff, ApplyLocation::WorkDir, Some(&mut opt))?;
|
|
|
|
Ok(())
|
|
} else {
|
|
Err(Error::Generic("hunk not found".to_string()))
|
|
}
|
|
}
|
|
|
|
fn find_hunk_index(diff: &Diff, hunk_hash: u64) -> Option<usize> {
|
|
let mut result = None;
|
|
|
|
let mut hunk_count = 0;
|
|
|
|
let foreach_result = diff.foreach(
|
|
&mut |_, _| true,
|
|
None,
|
|
Some(&mut |_, hunk| {
|
|
let header = HunkHeader::from(hunk);
|
|
if hash(&header) == hunk_hash {
|
|
result = Some(hunk_count);
|
|
}
|
|
hunk_count += 1;
|
|
true
|
|
}),
|
|
None,
|
|
);
|
|
|
|
if foreach_result.is_ok() {
|
|
result
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
///
|
|
pub fn unstage_hunk(
|
|
repo_path: &str,
|
|
file_path: String,
|
|
hunk_hash: u64,
|
|
) -> Result<bool> {
|
|
scope_time!("revert_hunk");
|
|
|
|
let repo = repo(repo_path)?;
|
|
|
|
let diff = get_diff_raw(&repo, &file_path, true, false)?;
|
|
let diff_count_positive = diff.deltas().len();
|
|
|
|
let hunk_index = find_hunk_index(&diff, hunk_hash);
|
|
if hunk_index.is_none() {
|
|
return Err(Error::Generic("hunk not found".to_string()));
|
|
}
|
|
|
|
let diff = get_diff_raw(&repo, &file_path, true, true)?;
|
|
|
|
assert_eq!(diff.deltas().len(), diff_count_positive);
|
|
|
|
let mut count = 0;
|
|
{
|
|
let mut hunk_idx = 0;
|
|
let mut opt = ApplyOptions::new();
|
|
opt.hunk_callback(|_hunk| {
|
|
let res = if hunk_idx == hunk_index.unwrap() {
|
|
count += 1;
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
hunk_idx += 1;
|
|
|
|
res
|
|
});
|
|
|
|
repo.apply(&diff, ApplyLocation::Index, Some(&mut opt))?;
|
|
}
|
|
|
|
Ok(count == 1)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::{
|
|
error::Result,
|
|
sync::{diff::get_diff, tests::repo_init_empty},
|
|
};
|
|
use std::{
|
|
fs::{self, File},
|
|
io::Write,
|
|
path::Path,
|
|
};
|
|
|
|
#[test]
|
|
fn reset_untracked_file_which_will_not_find_hunk() -> Result<()> {
|
|
let file_path = Path::new("foo/foo.txt");
|
|
let (_td, repo) = repo_init_empty()?;
|
|
let root = repo.path().parent().unwrap();
|
|
let repo_path = root.as_os_str().to_str().unwrap();
|
|
|
|
let sub_path = root.join("foo/");
|
|
|
|
fs::create_dir_all(&sub_path)?;
|
|
File::create(&root.join(file_path))?.write_all(b"test")?;
|
|
|
|
let diff = get_diff(
|
|
sub_path.to_str().unwrap(),
|
|
String::from(file_path.to_str().unwrap()),
|
|
false,
|
|
)?;
|
|
|
|
assert!(reset_hunk(
|
|
repo_path,
|
|
String::from(file_path.to_str().unwrap()),
|
|
diff.hunks[0].header_hash,
|
|
)
|
|
.is_err());
|
|
|
|
Ok(())
|
|
}
|
|
}
|