We have discovered that the change from 1->2 caused a bug.
There are a few ways to proceed.
This is business as usual and has nothing to do with subflakes.
We can use git to revert the entire repo to the earlier commit, and manually test in each location to see if the breakage we found in 2 is missing in 1.
Since foo is consumed by bar and then (transitively) by the toplevel flake, there are two places we can test to see if the bug is in foo.
First let's modify the toplevel flake to use the old foo:
diff --git a/flake.nix b/flake.nix
inputs = {
- bar.url = "path:./bar";
+ foo.url = "git+file:///Users/matt.rixman/2025/12/01/subflake_test_repo?rev=4426dcaf098c7c567aecb9aa6fce62d4fbf243f5&dir=foo";
+ bar = {
+ url = "path:./bar";
+ inputs.foo.follows = "foo";
+ };
In a real use case we'd be scrutinizing test results here.
We may even need to write a new test to run at the toplevel, just for identifying this bug.
In this case we're just looking at proof that the toplevel flake is indeed consuming the old foo.
❯ ls **/*.nix | each { |f|
{flake: $f.name
output: (cd ($f.name | path dirname) ; nix eval .#str)
}
}
╭───────────────┬───────────────╮
│ flake │ output │
├───────────────┼───────────────┤
│ flake.nix │ "foo1bar2baz" │
│ bar/flake.nix │ "foo2bar2" │
│ foo/flake.nix │ "foo2" │
╰───────────────┴───────────────╯
Hmm, we didn't find the bug.
Let's revert that and try modifying bar to use the old foo.
(The tests in bar might be more thorough than those at the top level.)
diff --git a/flake.nix b/flake.nix
inputs = {
- bar.url = "path:./bar";
+ foo.url = "git+file:///Users/matt.rixman/2025/12/01/subflake_test_repo?rev=4426dcaf098c7c567aecb9aa6fce62d4fbf243f5&dir=foo";
+ bar = {
+ url = "path:./bar";
+ inputs.foo.follows = "foo";
+ };
};
❯ ls **/*.nix | each { |f|
{flake: $f.name
output: (cd ($f.name | path dirname) ; nix eval .#str)
}
}
╭───────────────┬───────────────╮
│ flake │ output │
├───────────────┼───────────────┤
│ flake.nix │ "foo2bar2baz" │
│ bar/flake.nix │ "foo1bar2" │
│ foo/flake.nix │ "foo2" │
╰───────────────┴───────────────╯
Notice that at first, only bar picks up the change because that's the flake we modified.
The toplevel flake doesn't pick up the change because it's locked.
If we run nix flake update, we then find that the changes have propagated to the toplevel flake's output also:
╭───────────────┬───────────────╮
│ flake │ output │
├───────────────┼───────────────┤
│ flake.nix │ "foo1bar2baz" │
│ bar/flake.nix │ "foo1bar2" │
│ foo/flake.nix │ "foo2" │
╰───────────────┴───────────────╯
Try an Old Bar
This case is simpler, because only the toplevel flake depends on bar.
If we want to allow the old bar to depend on the old foo, like it authentically does, we can alter the toplevel flake like so:
inputs = {
- bar.url = "path:./bar";
+ bar.url = "git+file:///Users/matt.rixman/2025/12/01/subflake_test_repo?rev=4426dcaf098c7c567aecb9aa6fce62d4fbf243f5&dir=bar";
};
That will give us this:
╭───────────────┬───────────────╮
│ flake │ output │
├───────────────┼───────────────┤
│ flake.nix │ "foo1bar1baz" │
│ bar/flake.nix │ "foo2bar2" │
│ foo/flake.nix │ "foo2" │
╰───────────────┴───────────────╯
Alternatively, we can use the old bar, but supply it with a new foo:
inputs = {
- bar.url = "path:./bar";
+ foo.url = "path:./foo";
+ bar = {
+ url="git+file:///Users/matt.rixman/2025/12/01/subflake_test_repo?rev=4426dcaf098c7c567aecb9aa6fce62d4fbf243f5&dir=bar";
+ inputs.foo.follows = "foo";
+ };
};
This gives:
╭───────────────┬───────────────╮
│ flake │ output │
├───────────────┼───────────────┤
│ flake.nix │ "foo2bar1baz" │
│ bar/flake.nix │ "foo2bar2" │
│ foo/flake.nix │ "foo2" │
╰───────────────┴───────────────╯
Notice that bar/flake.nix still shows foo2bar2 because we've only modified the toplevel flake—the local bar subflake remains unchanged.
To verify the old bar code works with the new foo at the bar level, we need to evaluate the pinned bar directly.
This is worth doing because bar's own tests may catch integration issues that the toplevel tests miss—perhaps bar has unit tests that exercise edge cases the toplevel doesn't cover.
"foo2bar1"