moongitattrs

    Parse, evaluate, explain, and audit .gitattributes rules in MoonBit

    git
    gitattributes
    path-matching
    repository-tooling
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    5 hours ago
    Downloads
    3

    Dependencies

    #MoonGitAttrs

    MoonGitAttrs 是一个用 MoonBit 编写的 .gitattributes 解析、求值与检查工具。它可以回答“某个文件最终得到哪些属性、由哪一行规则决定”,也能发现常见的配置错误。核心库不访问文件系统,可在 wasmwasm-gcjsnative 后端使用;仓库同时提供一个 native CLI。

    #能做什么

    • 解析 Set、Unset、Value、Unspecified 四种 Git 属性状态;
    • 处理 *?、字符组和 ** 等路径模式;
    • 合并根目录、子目录和 .git/info/attributes 等多层来源;
    • 支持自定义属性宏和 Git 内置的 binary 宏;
    • 输出规则命中来源、行号和最终属性;
    • 检查非法 eol、重复赋值、保留属性名和容易误解的宏用法;
    • 生成稳定的文本、JSON 和 Markdown 报告。

    实现依据是 Git Attributes 官方文档。本项目为独立实现,没有复制 Git 的源代码。

    #适用场景

    1. 仓库维护者在 CI 中检查 .gitattributes,避免行尾、二进制文件或导出规则写错;
    2. 开发工具为指定路径展示最终属性和命中规则,定位“为什么 Git 这样处理这个文件”;
    3. 代码托管、归档或语言统计工具在不启动 Git 子进程的环境中预先求值属性;
    4. 教学和代码审查中演示多层规则的优先级与属性宏展开结果。

    #安装

    发布到 Mooncakes 后可添加依赖:

    moon add Xpeng/moongitattrs

    也可以克隆仓库直接运行:

    git clone https://github.com/pxgt/moongitattrs.git cd moongitattrs moon test --target all moon run cmd/main

    #库用法

    let rules = @moongitattrs.parse(
    #|[attr]docs text diff
    #|*.md docs eol=lf
    #|assets/** binary
    #|
    )

    let result = rules.evaluate("README.md")
    println(result.to_text())

    输出会包含最终状态及负责该状态的规则:

    path: README.md attributes: diff: set (.gitattributes:2, *.md) docs: set (.gitattributes:2, *.md) eol: lf (.gitattributes:2, *.md) text: set (.gitattributes:2, *.md)

    按 Git 的低到高优先级组合多个来源:

    let rules = @moongitattrs.parse_sources([
    @moongitattrs.AttributeSource::new(".gitattributes", "* text=auto\n"),
    @moongitattrs.AttributeSource::new(
    "docs/.gitattributes",
    "*.md eol=lf\n",
    base_dir="docs",
    ),
    @moongitattrs.AttributeSource::new(
    ".git/info/attributes",
    "README.md export-subst\n",
    ),
    ])

    #命令行用法

    仓库内的 examples/sample.gitattributes 可直接用于复现:

    # 查看某个路径的最终属性 moon run cmd/main -- explain examples/sample.gitattributes README.md # 输出机器可读 JSON moon run cmd/main -- explain-json examples/sample.gitattributes README.md # 检查配置;发现 error 时退出码为 2 moon run cmd/main -- audit examples/sample.gitattributes # 单独验证一个模式 moon run cmd/main -- match "docs/**" "docs/guide.md"

    #审计代码

    代码含义
    MGA001MGA008解析、模式或宏定义诊断
    MGA101同一条规则重复设置同一属性
    MGA102使用 Git 保留的 builtin_* 名称
    MGA103MGA104eol 值或写法可疑
    MGA105以不会展开的状态使用属性宏
    MGA106binarytext 在同一规则中冲突

    #项目边界

    MoonGitAttrs 负责解释配置,不修改工作区,也不执行换行转换、diff/merge 驱动或 GitHub Linguist 分类。库不会自动搜索磁盘上的配置文件;调用方需要按优先级把来源传给 parse_sources,CLI 则负责读取单个文件。详细设计见 docs/architecture.md

    #开发与验证

    moon fmt --check moon info --target all moon check --target all --deny-warn --warn-list +73 moon test --target all --deny-warn --warn-list +73 moon run cmd/main moon package --frozen

    完整验证记录和可复现步骤见 docs/verification.md。问题反馈请使用 GitHub Issues。安全问题请参阅 SECURITY.md

    #许可证

    Apache-2.0,见 LICENSE。第三方参考与来源说明见 THIRD_PARTY.md

    AttributeError

    pub(all) suberror AttributeError {
    InvalidAssignment(String)
    InvalidAttributeName(String)
    } derive(Eq,
    Debug
    )

    Assignment

    pub(all) struct Assignment {
    name : String
    state : AttributeState
    } derive(Eq, ToJson,
    Debug
    )

    One attribute operation attached to a pattern.

    Assignment::to_token

    fn Assignment::to_token(self : Assignment) -> String

    Converts an assignment back to its canonical token form.

    AttributeSource

    pub(all) struct AttributeSource {
    source : String
    base_dir : String
    content : String
    } derive(Eq,
    Debug
    )

    A .gitattributes source and the repository directory that contains it.

    AttributeSource::new

    fn AttributeSource::new(source : String, content : String, base_dir? : String) -> AttributeSource

    Creates a portable attribute source. base_dir is repository-relative and is empty for root, global, system, and $GIT_DIR/info/attributes files.

    AttributeState

    pub(all) enum AttributeState {
    Set
    Unset
    Value(String)
    Unspecified
    } derive(Eq, ToJson,
    Debug
    )

    The four states an attribute may have in Git's attribute model.

    AuditReport

    pub(all) struct AuditReport {
    diagnostics : Array[Diagnostic]
    findings : Array[Finding]
    } derive(Eq, ToJson,
    Debug
    )

    Repository-level audit result, including parser diagnostics.

    AuditReport::has_errors

    fn AuditReport::has_errors(self : AuditReport) -> Bool

    Returns true when parsing or policy auditing found an error.

    AuditReport::to_json_string

    fn AuditReport::to_json_string(self : AuditReport) -> String

    Renders an audit report as pretty JSON.

    AuditReport::to_markdown

    fn AuditReport::to_markdown(self : AuditReport) -> String

    Renders diagnostics and policy findings as a Markdown table.

    Diagnostic

    pub(all) struct Diagnostic {
    code : String
    severity : Severity
    message : String
    source : String
    line : Int
    column : Int
    } derive(Eq, ToJson,
    Debug
    )

    A stable, source-aware diagnostic.

    Evaluation

    pub(all) struct Evaluation {
    path : String
    attributes : Array[ResolvedAttribute]
    matched_rules : Array[Int]
    } derive(Eq, ToJson,
    Debug
    )

    Evaluation result for one repository-relative path.

    Evaluation::attribute

    fn Evaluation::attribute(self : Evaluation, name : String) -> AttributeState

    Returns the resolved state of name, or Unspecified when no rule decided it.

    Evaluation::to_json_string

    fn Evaluation::to_json_string(self : Evaluation) -> String

    Renders an evaluation as pretty JSON with attributes sorted by name.

    Evaluation::to_text

    fn Evaluation::to_text(self : Evaluation) -> String

    Renders an evaluation as compact, stable text suitable for terminals.

    Finding

    pub(all) struct Finding {
    code : String
    severity : Severity
    message : String
    source : String
    line : Int
    pattern : String
    attribute : String?
    } derive(Eq, ToJson,
    Debug
    )

    One actionable policy problem found in an attribute rule.

    MacroDefinition

    pub(all) struct MacroDefinition {
    name : String
    assignments : Array[Assignment]
    source : String
    line : Int
    } derive(Eq, ToJson,
    Debug
    )

    A named attribute macro declared by [attr]name.

    ResolvedAttribute

    pub(all) struct ResolvedAttribute {
    name : String
    state : AttributeState
    source : String
    line : Int
    pattern : String
    rule_index : Int
    } derive(Eq, ToJson,
    Debug
    )

    The rule responsible for the current value of an attribute.

    Rule

    pub(all) struct Rule {
    pattern : String
    assignments : Array[Assignment]
    source : String
    line : Int
    base_dir : String
    } derive(Eq, ToJson,
    Debug
    )

    A parsed attribute rule.

    RuleSet

    pub(all) struct RuleSet {
    rules : Array[Rule]
    macros : Array[MacroDefinition]
    diagnostics : Array[Diagnostic]
    } derive(Eq, ToJson,
    Debug
    )

    Result of parsing one or more attribute sources.

    RuleSet::audit

    fn RuleSet::audit(self : RuleSet) -> AuditReport

    Audits parsed rules for common configuration mistakes and ambiguous intent.

    RuleSet::evaluate

    fn RuleSet::evaluate(self : RuleSet, path : String) -> Evaluation

    Evaluates all matching rules for a repository-relative path.

    RuleSet::evaluate_many

    fn RuleSet::evaluate_many(self : RuleSet, paths : ArrayView[String]) -> Array[Evaluation]

    Evaluates a batch of paths while preserving input order.

    RuleSet::matching_rules

    fn RuleSet::matching_rules(self : RuleSet, path : String) -> Array[Rule]

    Returns all matching rules in evaluation order for inspection and tooling.

    Severity

    pub(all) enum Severity {
    Info
    Warning
    Error
    } derive(Eq, ToJson,
    Debug
    )

    Severity used by source diagnostics and policy findings.

    is_valid_attribute_name

    fn is_valid_attribute_name(name : String) -> Bool

    Returns whether name follows Git's attribute-name grammar.

    match_pattern

    fn match_pattern(pattern : String, path : String, base_dir? : String) -> Bool

    Matches one Git attribute pattern against a repository-relative path.

    A pattern without a slash is matched against every path component. A single * never crosses /, while ** may cross directory boundaries.

    parse

    fn parse(content : String, source? : String, base_dir? : String) -> RuleSet

    Parses one .gitattributes document without accessing the file system.

    parse_assignment

    fn parse_assignment(token : String) -> Assignment raise AttributeError

    Parses one attribute token such as text, -diff, eol=lf, or !merge.

    parse_sources

    fn parse_sources(sources : ArrayView[AttributeSource]) -> RuleSet

    Parses multiple sources in ascending precedence order.

    Pass system and global files first, followed by root and progressively deeper .gitattributes files. Pass $GIT_DIR/info/attributes last when it is available. Later matching assignments override earlier ones per attribute, matching Git's precedence model.