if-changed
if-changed is a command-line utility that checks for "if-changed" and "then-change" comments in a repository diff and errors if dependent files need changes.
Installation
Usage
)
To use with pre-commit, add the following to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/mathematic-inc/if-changed
rev: v0.3.2
hooks:
- id: if-changed
Motivating example
Suppose you have the following:
// lib.rs
// lib.ts
const enum ErrorCode {
A,
B,
C,
}
Typically, to synchronize these enums, a common approach is to extract the enum values into a "source-of-truth" file. This often requires significant effort to generate the enums using the build system or a script, and to ensure everything works correctly. If the job is a one-off, the costs heavily outweigh the benefits.
This is where if-changed comes in. Instead of the above, suppose we have:
// lib.rs
+// if-changed(ecrs)
enum ErrorCode {
A,
B,
C,
}
+// then-change(lib.ts:ects)
// lib.ts
+// if-changed(ects)
const enum ErrorCode {
A,
B,
C,
}
+// then-change(lib.rs:ecrs)
Once this is committed, the next time lib.rs (or lib.ts) is changed in the lines surrounded by "if-changed" and "then-change", if-changed will error if the other file (referenced in the "then-change" comment) does not have any changes in the corresponding named block.
[!TIP]
If you just want to assert that any change in a file is okay, then just reference the file without the name. For example,
// lib.ts // if-changed(ects) const enum ErrorCode { A, B, C, } -// then-change(lib.rs:ecrs) +// then-change(lib.rs)
File lists
If a block needs to specify several files, you can use commas and/or newlines to separate paths/patterns. For example,
// then-change(foo/bar, baz)
/// OR
// then-change(
// foo/bar
// bar
// )
These lists follow the same rules as .gitignore, with the exception that relative paths/patterns are always matched against the file in which they are written, even if the paths/patterns don't contain /. Use a starting / to match the pattern against the repository root, e.g. /*/bar.
Long paths
If a path is too long, you can use a shell continuation \ to split it across multiple lines. For example, for the path this/is/a/really/long/path/to/some/very/far/away/file, you can do
// then-change(
// this/is/a/really/long/path/to/some/very/far/ \
// away/file
// )
Disabling if-changed
To disable if-changed for a specific file during a commit, add Ignore-if-changed: <path>, ... -- [REASON] to the commit footer. Here, <path> names the file. In general, <path> can be any pattern allowed by fnmatch.
[!NOTE]
If you want to disable
if-changedwhen diffing the working tree, you can executeif-changedwith the following:where
<path-or-pattern>is the path/pattern you want to ignore.
Contributing
AI agents handle most maintenance and implementation for this repository. For us, reviewing an unsolicited pull request takes longer than implementing a proposal after we have agreed on it.
Start a Discussion and wait for a maintainer to review the proposal before doing implementation work. If we accept it, a Mathematic maintainer or agent will open the pull request. GitHub restricts pull request creation to Mathematic maintainers and repository collaborators who have write, maintain, or admin access, plus authorized maintenance agents.
When Mathematic implements a proposal, we will link the implementation pull request to the Discussion and credit the original author.
Read CONTRIBUTING.md for the full policy.
This project is free and open-source work by a 501(c)(3) non-profit. If you find it useful, please consider donating.