use crate::transaction_git::{GitContext, GitRequest, run_git};
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;
use std::path::Path;
pub const DEFAULT_MARKER_SIZE: usize = 7;
pub fn marker_size_of(value: &str) -> usize {
return match value.trim().parse::<usize>().ok() {
Some(size) if size > 0 => size,
_ => DEFAULT_MARKER_SIZE,
};
}
pub fn has_marker_lines(content: &[u8], size: usize) -> bool {
for line in content.split_inclusive(|byte| { return *byte == b'\n'; }) {
if line.len() <= size {
continue;
}
let first: u8 = line[0];
if first != b'<' && first != b'>' {
continue;
}
let all_same: bool = line[..size].iter().all(|byte| { return *byte == first; });
if all_same && line[size].is_ascii_whitespace() {
return true;
}
}
return false;
}
fn marker_sizes(context: &GitContext, root: &Path, paths: &[Vec<u8>]) -> Vec<usize> {
let mut arguments: Vec<OsString> = vec![OsString::from("check-attr"), OsString::from("-z"), OsString::from("conflict-marker-size"), OsString::from("--")];
arguments.extend(paths.iter().map(|path| { return OsString::from_vec(path.clone()); }));
let mut request: GitRequest = GitRequest::new(root, &[] as &[&str]);
request.arguments = arguments;
let answered: Vec<Vec<u8>> = match run_git(context, &request) {
Ok(output) if output.succeeded() => output.stdout.split(|byte| { return *byte == 0; }).map(<[u8]>::to_vec).collect(),
_ => Vec::new(),
};
return (0..paths.len())
.map(|index| {
let value: String = answered.get(index * 3 + 2).map(|bytes| { return String::from_utf8_lossy(bytes).into_owned(); }).unwrap_or_default();
return marker_size_of(value.as_str());
})
.collect();
}
pub fn paths_with_markers(context: &GitContext, root: &Path, commit: &str, paths: &[Vec<u8>]) -> Vec<Vec<u8>> {
if paths.is_empty() {
return Vec::new();
}
let sizes: Vec<usize> = marker_sizes(context, root, paths);
let mut marked: Vec<Vec<u8>> = Vec::new();
for (path, size) in paths.iter().zip(sizes) {
let mut name: Vec<u8> = format!("{commit}:").into_bytes();
name.extend_from_slice(path.as_slice());
let mut request: GitRequest = GitRequest::new(root, &[] as &[&str]);
request.arguments = vec![OsString::from("cat-file"), OsString::from("blob"), OsString::from_vec(name)];
if let Ok(output) = run_git(context, &request)
&& output.succeeded()
&& has_marker_lines(output.stdout.as_slice(), size)
{
marked.push(path.clone());
}
}
return marked;
}
pub fn open_operation_line(merge: bool) -> String {
let (operation, continuing): (&str, &str) = if merge { ("merge", "git merge --continue") } else { ("cherry-pick", "git cherry-pick --continue") };
return format!(
"cli-git: note: the {operation} is still in progress, because this commit took only the paths you named. To finish it, \
stage any file that is still unmerged (git add <path>), then run git commit with no paths, or {continuing}.\n"
);
}
#[cfg(test)]
#[path = "conflict_markers_tests.rs"]
mod tests;