use crates::git_checks_core::impl_prelude::*;
use crates::regex::Regex;
#[derive(Builder, Debug, Clone, Copy)]
#[builder(field(private))]
pub struct RejectConflictPaths {
#[builder(default = "true")]
require_base_exist: bool,
}
lazy_static! {
static ref CONFLICT_FILE_PATH: Regex = Regex::new(
"^(?P<base>.*)\
(?P<kind>_(BACKUP|BASE|LOCAL|REMOTE))\
(?P<pid>_[0-9]+)\
(?P<ext>(\\..*)?)$",
)
.unwrap();
}
const ORIG_SUFFIX: &str = ".orig";
impl RejectConflictPaths {
pub fn builder() -> RejectConflictPathsBuilder {
RejectConflictPathsBuilder::default()
}
fn check_conflict_path_name(
self,
ctx: &CheckGitContext,
path: &str,
) -> Result<bool, CommitError> {
if let Some(file_path) = CONFLICT_FILE_PATH.captures(path) {
if !self.require_base_exist {
return Ok(true);
}
let base = file_path
.name("base")
.expect("the conflict file path regex should have a 'base' group");
let ext = file_path
.name("ext")
.expect("the conflict file path regex should have a 'ext' group");
let basepath = format!("{}{}", base.as_str(), ext.as_str());
Self::check_for_path(ctx, &basepath)
} else {
Ok(false)
}
}
fn check_orig_path_name(self, ctx: &CheckGitContext, path: &str) -> Result<bool, CommitError> {
if path.ends_with(ORIG_SUFFIX) {
if !self.require_base_exist {
return Ok(true);
}
let basepath = path.trim_end_matches(ORIG_SUFFIX);
Self::check_for_path(ctx, basepath)
} else {
Ok(false)
}
}
fn check_for_path(ctx: &CheckGitContext, path: &str) -> Result<bool, CommitError> {
let cat_file = ctx
.git()
.arg("cat-file")
.arg("-e")
.arg(format!(":{}", path))
.output()
.map_err(|err| GitError::subcommand("cat-file -e", err))?;
Ok(cat_file.status.success())
}
}
impl Default for RejectConflictPaths {
fn default() -> Self {
RejectConflictPaths {
require_base_exist: true,
}
}
}
impl ContentCheck for RejectConflictPaths {
fn name(&self) -> &str {
"reject-conflict-paths"
}
fn check(
&self,
ctx: &CheckGitContext,
content: &dyn Content,
) -> Result<CheckResult, Box<dyn Error>> {
let mut result = CheckResult::new();
for diff in content.diffs() {
match diff.status {
StatusChange::Added | StatusChange::Modified(_) => (),
_ => continue,
}
if self.check_conflict_path_name(ctx, diff.name.as_str())? {
result.add_error(format!(
"{}it appears as though `{}` is a merge conflict \
resolution file and cannot be added.",
commit_prefix_str(content, "not allowed;"),
diff.name,
));
}
if self.check_orig_path_name(ctx, diff.name.as_str())? {
result.add_error(format!(
"{}it appears as though `{}` is a merge conflict \
backup file and cannot be added.",
commit_prefix_str(content, "not allowed;"),
diff.name,
));
}
}
Ok(result)
}
}
#[cfg(feature = "config")]
pub(crate) mod config {
use crates::git_checks_config::{CommitCheckConfig, IntoCheck, TopicCheckConfig};
use crates::inventory;
#[cfg(test)]
use crates::serde_json;
use RejectConflictPaths;
#[derive(Deserialize, Debug)]
pub struct RejectConflictPathsConfig {
#[serde(default)]
require_base_exist: Option<bool>,
}
impl IntoCheck for RejectConflictPathsConfig {
type Check = RejectConflictPaths;
fn into_check(self) -> Self::Check {
let mut builder = RejectConflictPaths::builder();
if let Some(require_base_exist) = self.require_base_exist {
builder.require_base_exist(require_base_exist);
}
builder
.build()
.expect("configuration mismatch for `RejectConflictPaths`")
}
}
register_checks! {
RejectConflictPathsConfig {
"reject_conflict_paths" => CommitCheckConfig,
"reject_conflict_paths/topic" => TopicCheckConfig,
},
}
#[test]
fn test_reject_conflict_paths_config_empty() {
let json = json!({});
serde_json::from_value::<RejectConflictPathsConfig>(json).unwrap();
}
#[test]
fn test_reject_conflict_paths_config_all_fields() {
let json = json!({
"require_base_exist": false,
});
serde_json::from_value::<RejectConflictPathsConfig>(json).unwrap();
}
}
#[cfg(test)]
mod tests {
use test::*;
use RejectConflictPaths;
const MERGE_CONFLICT_NO_BASE: &str = "52710df4a433731545ef99440edeb431b3160fc6";
const MERGE_CONFLICT_ORIG_NO_BASE: &str = "672a6a32b045bec6f93b9b1b5252611f40437a07";
const MERGE_CONFLICT_NO_EXT: &str = "f31b2e2cb75083d174097db7054cbc9e5836bad7";
const MERGE_CONFLICT_WITH_EXT: &str = "59f02bdb6c404c8f7cfb32700645c149148c089b";
const MERGE_CONFLICT_TWO_EXT: &str = "e9421eadfcac3c67a090444ef2ac859e86a8a2e0";
const MERGE_CONFLICT_ORIG_EXT: &str = "4abcf323b81c757e668ce1936f475b085d6852e8";
const MERGE_CONFLICT_NO_EXT_FIXED: &str = "63c923b5be729f672eddcd4b1e979a7d3d22606f";
const MERGE_CONFLICT_WITH_EXT_FIXED: &str = "8fcdd0d920ef47d0294229fdad4b3abd3ebdc43b";
const MERGE_CONFLICT_TWO_EXT_FIXED: &str = "0bf291f1f8a320abfb63776c56e1d1497231e24e";
const MERGE_CONFLICT_ORIG_EXT_FIXED: &str = "e1fd80490cd7f6556120aad6309d8fb95818b6ef";
#[test]
fn test_reject_conflict_paths_builder_default() {
assert!(RejectConflictPaths::builder().build().is_ok());
}
#[test]
fn test_reject_conflict_paths_no_base() {
let check = RejectConflictPaths::default();
run_check_ok(
"test_reject_conflict_paths_no_base",
MERGE_CONFLICT_NO_BASE,
check,
);
}
#[test]
fn test_reject_conflict_paths_no_base_topic() {
let check = RejectConflictPaths::default();
run_topic_check_ok(
"test_reject_conflict_paths_no_base_topic",
MERGE_CONFLICT_NO_BASE,
check,
);
}
#[test]
fn test_reject_conflict_paths_no_base_require() {
let check = RejectConflictPaths::builder()
.require_base_exist(false)
.build()
.unwrap();
let result = run_check(
"test_reject_conflict_paths_no_base_require",
MERGE_CONFLICT_NO_BASE,
check,
);
test_result_errors(result, &[
"commit 52710df4a433731545ef99440edeb431b3160fc6 not allowed; it appears as though \
`no_base_BACKUP_12345.ext` is a merge conflict resolution file and cannot be added.",
"commit 52710df4a433731545ef99440edeb431b3160fc6 not allowed; it appears as though \
`no_base_BASE_12345.ext` is a merge conflict resolution file and cannot be added.",
"commit 52710df4a433731545ef99440edeb431b3160fc6 not allowed; it appears as though \
`no_base_LOCAL_12345.ext` is a merge conflict resolution file and cannot be added.",
"commit 52710df4a433731545ef99440edeb431b3160fc6 not allowed; it appears as though \
`no_base_REMOTE_12345.ext` is a merge conflict resolution file and cannot be added.",
]);
}
#[test]
fn test_reject_conflict_paths_no_base_require_topic() {
let check = RejectConflictPaths::builder()
.require_base_exist(false)
.build()
.unwrap();
let result = run_topic_check(
"test_reject_conflict_paths_no_base_require_topic",
MERGE_CONFLICT_NO_BASE,
check,
);
test_result_errors(result, &[
"it appears as though `no_base_BACKUP_12345.ext` is a merge conflict resolution file \
and cannot be added.",
"it appears as though `no_base_BASE_12345.ext` is a merge conflict resolution file \
and cannot be added.",
"it appears as though `no_base_LOCAL_12345.ext` is a merge conflict resolution file \
and cannot be added.",
"it appears as though `no_base_REMOTE_12345.ext` is a merge conflict resolution file \
and cannot be added.",
]);
}
#[test]
fn test_reject_conflict_paths_orig_no_base() {
let check = RejectConflictPaths::default();
run_check_ok(
"test_reject_conflict_paths_orig_no_base",
MERGE_CONFLICT_ORIG_NO_BASE,
check,
);
}
#[test]
fn test_reject_conflict_paths_orig_no_base_topic() {
let check = RejectConflictPaths::default();
run_topic_check_ok(
"test_reject_conflict_paths_orig_no_base_topic",
MERGE_CONFLICT_ORIG_NO_BASE,
check,
);
}
#[test]
fn test_reject_conflict_paths_orig_no_base_require() {
let check = RejectConflictPaths::builder()
.require_base_exist(false)
.build()
.unwrap();
let result = run_check(
"test_reject_conflict_paths_orig_no_base_require",
MERGE_CONFLICT_ORIG_NO_BASE,
check,
);
test_result_errors(result, &[
"commit 672a6a32b045bec6f93b9b1b5252611f40437a07 not allowed; it appears as though \
`orig_file_valid.orig` is a merge conflict backup file and cannot be added.",
]);
}
#[test]
fn test_reject_conflict_paths_orig_no_base_require_topic() {
let check = RejectConflictPaths::builder()
.require_base_exist(false)
.build()
.unwrap();
let result = run_topic_check(
"test_reject_conflict_paths_orig_no_base_require_topic",
MERGE_CONFLICT_ORIG_NO_BASE,
check,
);
test_result_errors(
result,
&[
"it appears as though `orig_file_valid.orig` is a merge conflict backup file and \
cannot be added.",
],
);
}
#[test]
fn test_reject_conflict_paths_no_ext() {
let check = RejectConflictPaths::default();
let result = run_check(
"test_reject_conflict_paths_no_ext",
MERGE_CONFLICT_NO_EXT,
check,
);
test_result_errors(result, &[
"commit f31b2e2cb75083d174097db7054cbc9e5836bad7 not allowed; it appears as though \
`no_ext_BACKUP_12345` is a merge conflict resolution file and cannot be added.",
"commit f31b2e2cb75083d174097db7054cbc9e5836bad7 not allowed; it appears as though \
`no_ext_BASE_12345` is a merge conflict resolution file and cannot be added.",
"commit f31b2e2cb75083d174097db7054cbc9e5836bad7 not allowed; it appears as though \
`no_ext_LOCAL_12345` is a merge conflict resolution file and cannot be added.",
"commit f31b2e2cb75083d174097db7054cbc9e5836bad7 not allowed; it appears as though \
`no_ext_REMOTE_12345` is a merge conflict resolution file and cannot be added.",
]);
}
#[test]
fn test_reject_conflict_paths_no_ext_topic() {
let check = RejectConflictPaths::default();
let result = run_topic_check(
"test_reject_conflict_paths_no_ext_topic",
MERGE_CONFLICT_NO_EXT,
check,
);
test_result_errors(result, &[
"it appears as though `no_ext_BACKUP_12345` is a merge conflict resolution file and \
cannot be added.",
"it appears as though `no_ext_BASE_12345` is a merge conflict resolution file and \
cannot be added.",
"it appears as though `no_ext_LOCAL_12345` is a merge conflict resolution file and \
cannot be added.",
"it appears as though `no_ext_REMOTE_12345` is a merge conflict resolution file and \
cannot be added.",
]);
}
#[test]
fn test_reject_conflict_paths_no_ext_topic_fixed() {
let check = RejectConflictPaths::default();
run_topic_check_ok(
"test_reject_conflict_paths_no_ext_topic_fixed",
MERGE_CONFLICT_NO_EXT_FIXED,
check,
);
}
#[test]
fn test_reject_conflict_paths_with_ext() {
let check = RejectConflictPaths::default();
let result = run_check(
"test_reject_conflict_paths_with_ext",
MERGE_CONFLICT_WITH_EXT,
check,
);
test_result_errors(result, &[
"commit 59f02bdb6c404c8f7cfb32700645c149148c089b not allowed; it appears as though \
`conflict_with_BACKUP_12345.ext` is a merge conflict resolution file and cannot be \
added.",
"commit 59f02bdb6c404c8f7cfb32700645c149148c089b not allowed; it appears as though \
`conflict_with_BASE_12345.ext` is a merge conflict resolution file and cannot be \
added.",
"commit 59f02bdb6c404c8f7cfb32700645c149148c089b not allowed; it appears as though \
`conflict_with_LOCAL_12345.ext` is a merge conflict resolution file and cannot be \
added.",
"commit 59f02bdb6c404c8f7cfb32700645c149148c089b not allowed; it appears as though \
`conflict_with_REMOTE_12345.ext` is a merge conflict resolution file and cannot be \
added.",
]);
}
#[test]
fn test_reject_conflict_paths_with_ext_topic() {
let check = RejectConflictPaths::default();
let result = run_topic_check(
"test_reject_conflict_paths_with_ext_topic",
MERGE_CONFLICT_WITH_EXT,
check,
);
test_result_errors(result, &[
"it appears as though `conflict_with_BACKUP_12345.ext` is a merge conflict resolution \
file and cannot be added.",
"it appears as though `conflict_with_BASE_12345.ext` is a merge conflict resolution \
file and cannot be added.",
"it appears as though `conflict_with_LOCAL_12345.ext` is a merge conflict resolution \
file and cannot be added.",
"it appears as though `conflict_with_REMOTE_12345.ext` is a merge conflict resolution \
file and cannot be added.",
]);
}
#[test]
fn test_reject_conflict_paths_with_ext_topic_fixed() {
let check = RejectConflictPaths::default();
run_topic_check_ok(
"test_reject_conflict_paths_with_ext_topic_fixed",
MERGE_CONFLICT_WITH_EXT_FIXED,
check,
);
}
#[test]
fn test_reject_conflict_paths_two_ext() {
let check = RejectConflictPaths::default();
let result = run_check(
"test_reject_conflict_paths_two_ext",
MERGE_CONFLICT_TWO_EXT,
check,
);
test_result_errors(result, &[
"commit e9421eadfcac3c67a090444ef2ac859e86a8a2e0 not allowed; it appears as though \
`conflict_with.two_BACKUP_12345.ext` is a merge conflict resolution file and cannot \
be added.",
"commit e9421eadfcac3c67a090444ef2ac859e86a8a2e0 not allowed; it appears as though \
`conflict_with.two_BASE_12345.ext` is a merge conflict resolution file and cannot be \
added.",
"commit e9421eadfcac3c67a090444ef2ac859e86a8a2e0 not allowed; it appears as though \
`conflict_with.two_LOCAL_12345.ext` is a merge conflict resolution file and cannot be \
added.",
"commit e9421eadfcac3c67a090444ef2ac859e86a8a2e0 not allowed; it appears as though \
`conflict_with.two_REMOTE_12345.ext` is a merge conflict resolution file and cannot \
be added.",
]);
}
#[test]
fn test_reject_conflict_paths_two_ext_topic() {
let check = RejectConflictPaths::default();
let result = run_topic_check(
"test_reject_conflict_paths_two_ext_topic",
MERGE_CONFLICT_TWO_EXT,
check,
);
test_result_errors(
result,
&[
"it appears as though `conflict_with.two_BACKUP_12345.ext` is a merge conflict \
resolution file and cannot be added.",
"it appears as though `conflict_with.two_BASE_12345.ext` is a merge conflict \
resolution file and cannot be added.",
"it appears as though `conflict_with.two_LOCAL_12345.ext` is a merge conflict \
resolution file and cannot be added.",
"it appears as though `conflict_with.two_REMOTE_12345.ext` is a merge conflict \
resolution file and cannot be added.",
],
);
}
#[test]
fn test_reject_conflict_paths_two_ext_topic_fixed() {
let check = RejectConflictPaths::default();
run_topic_check_ok(
"test_reject_conflict_paths_two_ext_topic_fixed",
MERGE_CONFLICT_TWO_EXT_FIXED,
check,
);
}
#[test]
fn test_reject_conflict_paths_orig_ext() {
let check = RejectConflictPaths::default();
let result = run_check(
"test_reject_conflict_paths_orig_ext",
MERGE_CONFLICT_ORIG_EXT,
check,
);
test_result_errors(result, &[
"commit 4abcf323b81c757e668ce1936f475b085d6852e8 not allowed; it appears as though \
`orig_file.ext.orig` is a merge conflict backup file and cannot be added.",
]);
}
#[test]
fn test_reject_conflict_paths_orig_ext_topic() {
let check = RejectConflictPaths::default();
let result = run_topic_check(
"test_reject_conflict_paths_orig_ext_topic",
MERGE_CONFLICT_ORIG_EXT,
check,
);
test_result_errors(result, &[
"it appears as though `orig_file.ext.orig` is a merge conflict backup file and cannot \
be added.",
]);
}
#[test]
fn test_reject_conflict_paths_orig_ext_topic_fixed() {
let check = RejectConflictPaths::default();
run_topic_check_ok(
"test_reject_conflict_paths_orig_ext_topic_fixed",
MERGE_CONFLICT_ORIG_EXT_FIXED,
check,
);
}
}