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.1
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 commited, 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 ok, 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>
should be the file path. In general, <path>
can be any pattern allowed by fnmatch.
[!NOTE]
If you want to disable
if-changed
when diffing the working tree, you can executeif-changed
with the following:where
<path-or-pattern>
is the path/pattern you want to ignore.
Contributing
Contributions to if-changed
are welcome! Please submit a pull request or create an issue in the GitHub repository.