diff --git a/tests/flake-bundler.sh b/tests/flakes/bundle.sh
similarity index 84%
rename from tests/flake-bundler.sh
rename to tests/flakes/bundle.sh
index 9496b8f92..67bbb05ac 100644
--- a/tests/flake-bundler.sh
+++ b/tests/flakes/bundle.sh
@@ -1,9 +1,6 @@
 source common.sh
 
-clearStore
-rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
-
-cp ./simple.nix ./simple.builder.sh ./config.nix $TEST_HOME
+cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME
 
 cd $TEST_HOME
 
@@ -25,6 +22,7 @@ cat <<EOF > flake.nix
     };
 }
 EOF
+
 nix build .#
 nix bundle --bundler .# .#
 nix bundle --bundler .#bundlers.$system.default .#packages.$system.default
@@ -32,6 +30,3 @@ nix bundle --bundler .#bundlers.$system.simple  .#packages.$system.default
 
 nix bundle --bundler .#bundlers.$system.default .#apps.$system.default
 nix bundle --bundler .#bundlers.$system.simple  .#apps.$system.default
-
-clearStore
-
diff --git a/tests/flakes/check.sh b/tests/flakes/check.sh
new file mode 100644
index 000000000..f572aa75c
--- /dev/null
+++ b/tests/flakes/check.sh
@@ -0,0 +1,89 @@
+source common.sh
+
+flakeDir=$TEST_ROOT/flake3
+mkdir -p $flakeDir
+
+cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    overlay = final: prev: {
+    };
+  };
+}
+EOF
+
+nix flake check $flakeDir
+
+cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    overlay = finalll: prev: {
+    };
+  };
+}
+EOF
+
+(! nix flake check $flakeDir)
+
+cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    nixosModules.foo = {
+      a.b.c = 123;
+      foo = true;
+    };
+  };
+}
+EOF
+
+nix flake check $flakeDir
+
+cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    nixosModules.foo = {
+      a.b.c = 123;
+      foo = assert false; true;
+    };
+  };
+}
+EOF
+
+(! nix flake check $flakeDir)
+
+cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    nixosModule = { config, pkgs, ... }: {
+      a.b.c = 123;
+    };
+  };
+}
+EOF
+
+nix flake check $flakeDir
+
+cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    nixosModule = { config, pkgs }: {
+      a.b.c = 123;
+    };
+  };
+}
+EOF
+
+(! nix flake check $flakeDir)
+
+cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    packages.system-1.default = "foo";
+    packages.system-2.default = "bar";
+  };
+}
+EOF
+
+checkRes=$(nix flake check --keep-going $flakeDir 2>&1 && fail "nix flake check should have failed" || true)
+echo "$checkRes" | grep -q "packages.system-1.default"
+echo "$checkRes" | grep -q "packages.system-2.default"
diff --git a/tests/flakes/circular.sh b/tests/flakes/circular.sh
new file mode 100644
index 000000000..09cd02edf
--- /dev/null
+++ b/tests/flakes/circular.sh
@@ -0,0 +1,49 @@
+# Test circular flake dependencies.
+source ./common.sh
+
+requireGit
+
+flakeA=$TEST_ROOT/flakeA
+flakeB=$TEST_ROOT/flakeB
+
+createGitRepo $flakeA
+createGitRepo $flakeB
+
+cat > $flakeA/flake.nix <<EOF
+{
+  inputs.b.url = git+file://$flakeB;
+  inputs.b.inputs.a.follows = "/";
+
+  outputs = { self, b }: {
+    foo = 123 + b.bar;
+    xyzzy = 1000;
+  };
+}
+EOF
+
+git -C $flakeA add flake.nix
+
+cat > $flakeB/flake.nix <<EOF
+{
+  inputs.a.url = git+file://$flakeA;
+
+  outputs = { self, a }: {
+    bar = 456 + a.xyzzy;
+  };
+}
+EOF
+
+git -C $flakeB add flake.nix
+git -C $flakeB commit -a -m 'Foo'
+
+[[ $(nix eval $flakeA#foo) = 1579 ]]
+[[ $(nix eval $flakeA#foo) = 1579 ]]
+
+sed -i $flakeB/flake.nix -e 's/456/789/'
+git -C $flakeB commit -a -m 'Foo'
+
+[[ $(nix eval --update-input b $flakeA#foo) = 1912 ]]
+
+# Test list-inputs with circular dependencies
+nix flake metadata $flakeA
+
diff --git a/tests/flakes/common.sh b/tests/flakes/common.sh
new file mode 100644
index 000000000..c333733c2
--- /dev/null
+++ b/tests/flakes/common.sh
@@ -0,0 +1,73 @@
+source ../common.sh
+
+registry=$TEST_ROOT/registry.json
+
+requireGit() {
+    if [[ -z $(type -p git) ]]; then
+        echo "Git not installed; skipping flake tests"
+        exit 99
+    fi
+}
+
+writeSimpleFlake() {
+    local flakeDir="$1"
+    cat > $flakeDir/flake.nix <<EOF
+{
+  description = "Bla bla";
+
+  outputs = inputs: rec {
+    packages.$system = rec {
+      foo = import ./simple.nix;
+      default = foo;
+    };
+
+    # To test "nix flake init".
+    legacyPackages.x86_64-linux.hello = import ./simple.nix;
+  };
+}
+EOF
+
+    cp ../simple.nix ../simple.builder.sh ../config.nix $flakeDir/
+}
+
+createSimpleGitFlake() {
+    local flakeDir="$1"
+    writeSimpleFlake $flakeDir
+    git -C $flakeDir add flake.nix simple.nix simple.builder.sh config.nix
+    git -C $flakeDir commit -m 'Initial'
+}
+
+writeDependentFlake() {
+    local flakeDir="$1"
+    cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self, flake1 }: {
+    packages.$system.default = flake1.packages.$system.default;
+    expr = assert builtins.pathExists ./flake.lock; 123;
+  };
+}
+EOF
+}
+
+writeTrivialFlake() {
+    local flakeDir="$1"
+    cat > $flakeDir/flake.nix <<EOF
+{
+  outputs = { self }: {
+    expr = 123;
+  };
+}
+EOF
+}
+
+createGitRepo() {
+    local repo="$1"
+    local extraArgs="$2"
+
+    rm -rf $repo $repo.tmp
+    mkdir -p $repo
+
+    git -C $repo init $extraArgs
+    git -C $repo config user.email "foobar@example.com"
+    git -C $repo config user.name "Foobar"
+}
diff --git a/tests/flake-local-settings.sh b/tests/flakes/config.sh
similarity index 88%
rename from tests/flake-local-settings.sh
rename to tests/flakes/config.sh
index e92c16f87..d1941a6be 100644
--- a/tests/flake-local-settings.sh
+++ b/tests/flakes/config.sh
@@ -1,9 +1,6 @@
 source common.sh
 
-clearStore
-rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
-
-cp ./simple.nix ./simple.builder.sh ./config.nix $TEST_HOME
+cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME
 
 cd $TEST_HOME
 
diff --git a/tests/flakes.sh b/tests/flakes/flakes.sh
similarity index 57%
rename from tests/flakes.sh
rename to tests/flakes/flakes.sh
index 6574cabf1..267e2cd6f 100644
--- a/tests/flakes.sh
+++ b/tests/flakes/flakes.sh
@@ -1,74 +1,30 @@
-source common.sh
+source ./common.sh
 
-if [[ -z $(type -p git) ]]; then
-    echo "Git not installed; skipping flake tests"
-    exit 99
-fi
+requireGit
 
 clearStore
 rm -rf $TEST_HOME/.cache $TEST_HOME/.config
 
-registry=$TEST_ROOT/registry.json
-
 flake1Dir=$TEST_ROOT/flake1
 flake2Dir=$TEST_ROOT/flake2
 flake3Dir=$TEST_ROOT/flake3
 flake5Dir=$TEST_ROOT/flake5
-flake6Dir=$TEST_ROOT/flake6
 flake7Dir=$TEST_ROOT/flake7
-templatesDir=$TEST_ROOT/templates
 nonFlakeDir=$TEST_ROOT/nonFlake
 badFlakeDir=$TEST_ROOT/badFlake
-flakeA=$TEST_ROOT/flakeA
-flakeB=$TEST_ROOT/flakeB
 flakeGitBare=$TEST_ROOT/flakeGitBare
-flakeFollowsA=$TEST_ROOT/follows/flakeA
-flakeFollowsB=$TEST_ROOT/follows/flakeA/flakeB
-flakeFollowsC=$TEST_ROOT/follows/flakeA/flakeB/flakeC
-flakeFollowsD=$TEST_ROOT/follows/flakeA/flakeD
-flakeFollowsE=$TEST_ROOT/follows/flakeA/flakeE
-
-initRepo() {
-    local repo="$1"
-    local extraArgs="$2"
-
-    git -C $repo init $extraArgs
-    git -C $repo config user.email "foobar@example.com"
-    git -C $repo config user.name "Foobar"
-}
-
-for repo in $flake1Dir $flake2Dir $flake3Dir $flake7Dir $templatesDir $nonFlakeDir $flakeA $flakeB $flakeFollowsA; do
-    rm -rf $repo $repo.tmp
-    mkdir -p $repo
 
+for repo in $flake1Dir $flake2Dir $flake3Dir $flake7Dir $nonFlakeDir; do
     # Give one repo a non-main initial branch.
     extraArgs=
     if [[ $repo == $flake2Dir ]]; then
       extraArgs="--initial-branch=main"
     fi
 
-    initRepo "$repo" "$extraArgs"
+    createGitRepo "$repo" "$extraArgs"
 done
 
-cat > $flake1Dir/flake.nix <<EOF
-{
-  description = "Bla bla";
-
-  outputs = inputs: rec {
-    packages.$system = rec {
-      foo = import ./simple.nix;
-      default = foo;
-    };
-
-    # To test "nix flake init".
-    legacyPackages.x86_64-linux.hello = import ./simple.nix;
-  };
-}
-EOF
-
-cp ./simple.nix ./simple.builder.sh ./config.nix $flake1Dir/
-git -C $flake1Dir add flake.nix simple.nix simple.builder.sh config.nix
-git -C $flake1Dir commit -m 'Initial'
+createSimpleGitFlake $flake1Dir
 
 cat > $flake2Dir/flake.nix <<EOF
 {
@@ -112,12 +68,10 @@ nix registry add --registry $registry flake1 git+file://$flake1Dir
 nix registry add --registry $registry flake2 git+file://$flake2Dir
 nix registry add --registry $registry flake3 git+file://$flake3Dir
 nix registry add --registry $registry flake4 flake3
-nix registry add --registry $registry flake5 hg+file://$flake5Dir
 nix registry add --registry $registry nixpkgs flake1
-nix registry add --registry $registry templates git+file://$templatesDir
 
 # Test 'nix flake list'.
-[[ $(nix registry list | wc -l) == 7 ]]
+[[ $(nix registry list | wc -l) == 5 ]]
 
 # Test 'nix flake metadata'.
 nix flake metadata flake1
@@ -298,7 +252,7 @@ cat > $flake3Dir/flake.nix <<EOF
 }
 EOF
 
-cp ./config.nix $flake3Dir
+cp ../config.nix $flake3Dir
 
 git -C $flake3Dir add flake.nix config.nix
 git -C $flake3Dir commit -m 'Add nonFlakeInputs'
@@ -373,176 +327,19 @@ nix build -o $TEST_ROOT/result flake4/removeXyzzy#sth
 
 # Testing the nix CLI
 nix registry add flake1 flake3
-[[ $(nix registry list | wc -l) == 8 ]]
+[[ $(nix registry list | wc -l) == 6 ]]
 nix registry pin flake1
-[[ $(nix registry list | wc -l) == 8 ]]
+[[ $(nix registry list | wc -l) == 6 ]]
 nix registry pin flake1 flake3
-[[ $(nix registry list | wc -l) == 8 ]]
+[[ $(nix registry list | wc -l) == 6 ]]
 nix registry remove flake1
-[[ $(nix registry list | wc -l) == 7 ]]
-
-# Test 'nix flake init'.
-cat > $templatesDir/flake.nix <<EOF
-{
-  description = "Some templates";
-
-  outputs = { self }: {
-    templates = rec {
-      trivial = {
-        path = ./trivial;
-        description = "A trivial flake";
-        welcomeText = ''
-            Welcome to my trivial flake
-        '';
-      };
-      default = trivial;
-    };
-  };
-}
-EOF
-
-mkdir $templatesDir/trivial
-
-cat > $templatesDir/trivial/flake.nix <<EOF
-{
-  description = "A flake for building Hello World";
-
-  outputs = { self, nixpkgs }: {
-    packages.x86_64-linux = rec {
-      hello = nixpkgs.legacyPackages.x86_64-linux.hello;
-      default = hello;
-    };
-  };
-}
-EOF
-echo a > $templatesDir/trivial/a
-echo b > $templatesDir/trivial/b
-
-git -C $templatesDir add flake.nix trivial/
-git -C $templatesDir commit -m 'Initial'
-
-nix flake check templates
-nix flake show templates
-nix flake show templates --json | jq
-
-(cd $flake7Dir && nix flake init)
-(cd $flake7Dir && nix flake init) # check idempotence
-git -C $flake7Dir add flake.nix
-nix flake check $flake7Dir
-nix flake show $flake7Dir
-nix flake show $flake7Dir --json | jq
-git -C $flake7Dir commit -a -m 'Initial'
-
-# Test 'nix flake init' with benign conflicts
-rm -rf $flake7Dir && mkdir $flake7Dir && initRepo "$flake7Dir"
-echo a > $flake7Dir/a
-(cd $flake7Dir && nix flake init) # check idempotence
-
-# Test 'nix flake init' with conflicts
-rm -rf $flake7Dir && mkdir $flake7Dir && initRepo "$flake7Dir"
-echo b > $flake7Dir/a
-pushd $flake7Dir
-(! nix flake init) |& grep "refusing to overwrite existing file '$flake7Dir/a'"
-popd
-git -C $flake7Dir commit -a -m 'Changed'
-
-# Test 'nix flake new'.
-rm -rf $flake6Dir
-nix flake new -t templates#trivial $flake6Dir
-nix flake new -t templates#trivial $flake6Dir # check idempotence
-nix flake check $flake6Dir
+[[ $(nix registry list | wc -l) == 5 ]]
 
 # Test 'nix flake clone'.
 rm -rf $TEST_ROOT/flake1-v2
 nix flake clone flake1 --dest $TEST_ROOT/flake1-v2
 [ -e $TEST_ROOT/flake1-v2/flake.nix ]
 
-# More 'nix flake check' tests.
-cat > $flake3Dir/flake.nix <<EOF
-{
-  outputs = { flake1, self }: {
-    overlay = final: prev: {
-    };
-  };
-}
-EOF
-
-nix flake check $flake3Dir
-
-cat > $flake3Dir/flake.nix <<EOF
-{
-  outputs = { flake1, self }: {
-    overlay = finalll: prev: {
-    };
-  };
-}
-EOF
-
-(! nix flake check $flake3Dir)
-
-cat > $flake3Dir/flake.nix <<EOF
-{
-  outputs = { flake1, self }: {
-    nixosModules.foo = {
-      a.b.c = 123;
-      foo = true;
-    };
-  };
-}
-EOF
-
-nix flake check $flake3Dir
-
-cat > $flake3Dir/flake.nix <<EOF
-{
-  outputs = { flake1, self }: {
-    nixosModules.foo = {
-      a.b.c = 123;
-      foo = assert false; true;
-    };
-  };
-}
-EOF
-
-(! nix flake check $flake3Dir)
-
-cat > $flake3Dir/flake.nix <<EOF
-{
-  outputs = { flake1, self }: {
-    nixosModule = { config, pkgs, ... }: {
-      a.b.c = 123;
-    };
-  };
-}
-EOF
-
-nix flake check $flake3Dir
-
-cat > $flake3Dir/flake.nix <<EOF
-{
-  outputs = { flake1, self }: {
-    nixosModule = { config, pkgs }: {
-      a.b.c = 123;
-    };
-  };
-}
-EOF
-
-(! nix flake check $flake3Dir)
-
-cat > $flake3Dir/flake.nix <<EOF
-{
-  outputs = { flake1, self }: {
-    packages.system-1.default = "foo";
-    packages.system-2.default = "bar";
-  };
-}
-EOF
-
-checkRes=$(nix flake check --keep-going $flake3Dir 2>&1 && fail "nix flake check should have failed" || true)
-echo "$checkRes" | grep -q "packages.system-1.default"
-echo "$checkRes" | grep -q "packages.system-2.default"
-
 # Test 'follows' inputs.
 cat > $flake3Dir/flake.nix <<EOF
 {
@@ -585,6 +382,10 @@ nix flake lock $flake3Dir
 [[ $(jq -c .nodes.root.inputs.bar $flake3Dir/flake.lock) = '["flake2"]' ]]
 
 # Test overriding inputs of inputs.
+writeTrivialFlake $flake7Dir
+git -C $flake7Dir add flake.nix
+git -C $flake7Dir commit -m 'Initial'
+
 cat > $flake3Dir/flake.nix <<EOF
 {
   inputs.flake2.inputs.flake1 = {
@@ -619,50 +420,9 @@ rm -rf $flakeGitBare
 git clone --bare $flake1Dir $flakeGitBare
 nix build -o $TEST_ROOT/result git+file://$flakeGitBare
 
-# Test Mercurial flakes.
-rm -rf $flake5Dir
-mkdir $flake5Dir
-
-cat > $flake5Dir/flake.nix <<EOF
-{
-  outputs = { self, flake1 }: {
-    packages.$system.default = flake1.packages.$system.default;
-    expr = assert builtins.pathExists ./flake.lock; 123;
-  };
-}
-EOF
-
-if [[ -n $(type -p hg) ]]; then
-    hg init $flake5Dir
-
-    hg add $flake5Dir/flake.nix
-    hg commit --config ui.username=foobar@example.org $flake5Dir -m 'Initial commit'
-
-    nix build -o $TEST_ROOT/result hg+file://$flake5Dir
-    [[ -e $TEST_ROOT/result/hello ]]
-
-    (! nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revision)
-
-    nix eval hg+file://$flake5Dir#expr
-
-    nix eval hg+file://$flake5Dir#expr
-
-    (! nix eval hg+file://$flake5Dir#expr --no-allow-dirty)
-
-    (! nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revision)
-
-    hg commit --config ui.username=foobar@example.org $flake5Dir -m 'Add lock file'
-
-    nix flake metadata --json hg+file://$flake5Dir --refresh | jq -e -r .revision
-    nix flake metadata --json hg+file://$flake5Dir
-    [[ $(nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revCount) = 1 ]]
-
-    nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-registries --no-allow-dirty
-    nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-use-registries --no-allow-dirty
-fi
-
 # Test path flakes.
-rm -rf $flake5Dir/.hg $flake5Dir/flake.lock
+mkdir -p $flake5Dir
+writeDependentFlake $flake5Dir
 nix flake lock path://$flake5Dir
 
 # Test tarball flakes.
@@ -700,166 +460,6 @@ nix flake lock $flake3Dir --update-input flake2/flake1
 # Test 'nix flake metadata --json'.
 nix flake metadata $flake3Dir --json | jq .
 
-# Test circular flake dependencies.
-cat > $flakeA/flake.nix <<EOF
-{
-  inputs.b.url = git+file://$flakeB;
-  inputs.b.inputs.a.follows = "/";
-
-  outputs = { self, nixpkgs, b }: {
-    foo = 123 + b.bar;
-    xyzzy = 1000;
-  };
-}
-EOF
-
-git -C $flakeA add flake.nix
-
-cat > $flakeB/flake.nix <<EOF
-{
-  inputs.a.url = git+file://$flakeA;
-
-  outputs = { self, nixpkgs, a }: {
-    bar = 456 + a.xyzzy;
-  };
-}
-EOF
-
-git -C $flakeB add flake.nix
-git -C $flakeB commit -a -m 'Foo'
-
-[[ $(nix eval $flakeA#foo) = 1579 ]]
-[[ $(nix eval $flakeA#foo) = 1579 ]]
-
-sed -i $flakeB/flake.nix -e 's/456/789/'
-git -C $flakeB commit -a -m 'Foo'
-
-[[ $(nix eval --update-input b $flakeA#foo) = 1912 ]]
-
-# Test list-inputs with circular dependencies
-nix flake metadata $flakeA
-
-# Test flake follow paths
-mkdir -p $flakeFollowsB
-mkdir -p $flakeFollowsC
-mkdir -p $flakeFollowsD
-mkdir -p $flakeFollowsE
-
-cat > $flakeFollowsA/flake.nix <<EOF
-{
-    description = "Flake A";
-    inputs = {
-        B = {
-            url = "path:./flakeB";
-            inputs.foobar.follows = "foobar";
-        };
-
-        foobar.url = "path:$flakeFollowsA/flakeE";
-    };
-    outputs = { ... }: {};
-}
-EOF
-
-cat > $flakeFollowsB/flake.nix <<EOF
-{
-    description = "Flake B";
-    inputs = {
-        foobar.url = "path:$flakeFollowsA/flakeE";
-        goodoo.follows = "C/goodoo";
-        C = {
-            url = "path:./flakeC";
-            inputs.foobar.follows = "foobar";
-        };
-    };
-    outputs = { ... }: {};
-}
-EOF
-
-cat > $flakeFollowsC/flake.nix <<EOF
-{
-    description = "Flake C";
-    inputs = {
-        foobar.url = "path:$flakeFollowsA/flakeE";
-        goodoo.follows = "foobar";
-    };
-    outputs = { ... }: {};
-}
-EOF
-
-cat > $flakeFollowsD/flake.nix <<EOF
-{
-    description = "Flake D";
-    inputs = {};
-    outputs = { ... }: {};
-}
-EOF
-
-cat > $flakeFollowsE/flake.nix <<EOF
-{
-    description = "Flake E";
-    inputs = {};
-    outputs = { ... }: {};
-}
-EOF
-
-git -C $flakeFollowsA add flake.nix flakeB/flake.nix \
-  flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix
-
-nix flake metadata $flakeFollowsA
-
-nix flake update $flakeFollowsA
-
-nix flake lock $flakeFollowsA
-
-oldLock="$(cat "$flakeFollowsA/flake.lock")"
-
-# Ensure that locking twice doesn't change anything
-
-nix flake lock $flakeFollowsA
-
-newLock="$(cat "$flakeFollowsA/flake.lock")"
-
-diff <(echo "$newLock") <(echo "$oldLock")
-
-[[ $(jq -c .nodes.B.inputs.C $flakeFollowsA/flake.lock) = '"C"' ]]
-[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '["foobar"]' ]]
-[[ $(jq -c .nodes.C.inputs.foobar $flakeFollowsA/flake.lock) = '["B","foobar"]' ]]
-
-# Ensure removing follows from flake.nix removes them from the lockfile
-
-cat > $flakeFollowsA/flake.nix <<EOF
-{
-    description = "Flake A";
-    inputs = {
-        B = {
-            url = "path:./flakeB";
-        };
-        D.url = "path:./flakeD";
-    };
-    outputs = { ... }: {};
-}
-EOF
-
-nix flake lock $flakeFollowsA
-
-[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '"foobar"' ]]
-jq -r -c '.nodes | keys | .[]' $flakeFollowsA/flake.lock | grep "^foobar$"
-
-# Ensure a relative path is not allowed to go outside the store path
-cat > $flakeFollowsA/flake.nix <<EOF
-{
-    description = "Flake A";
-    inputs = {
-        B.url = "path:../flakeB";
-    };
-    outputs = { ... }: {};
-}
-EOF
-
-git -C $flakeFollowsA add flake.nix
-
-nix flake lock $flakeFollowsA 2>&1 | grep 'points outside'
-
 # Test flake in store does not evaluate
 rm -rf $badFlakeDir
 mkdir $badFlakeDir
@@ -868,23 +468,3 @@ nix store delete $(nix store add-path $badFlakeDir)
 
 [[ $(nix path-info      $(nix store add-path $flake1Dir)) =~ flake1 ]]
 [[ $(nix path-info path:$(nix store add-path $flake1Dir)) =~ simple ]]
-
-# Non-existant follows causes an error
-
-cat >$flakeFollowsA/flake.nix <<EOF
-{
-    description = "Flake A";
-    inputs.B = {
-        url = "path:./flakeB";
-        inputs.invalid.follows = "D";
-        inputs.invalid2.url = "path:./flakeD";
-    };
-    inputs.D.url = "path:./flakeD";
-    outputs = { ... }: {};
-}
-EOF
-
-git -C $flakeFollowsA add flake.nix
-
-nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'"
-nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'"
diff --git a/tests/flakes/follow-paths.sh b/tests/flakes/follow-paths.sh
new file mode 100644
index 000000000..19cc1bafa
--- /dev/null
+++ b/tests/flakes/follow-paths.sh
@@ -0,0 +1,150 @@
+source ./common.sh
+
+requireGit
+
+flakeFollowsA=$TEST_ROOT/follows/flakeA
+flakeFollowsB=$TEST_ROOT/follows/flakeA/flakeB
+flakeFollowsC=$TEST_ROOT/follows/flakeA/flakeB/flakeC
+flakeFollowsD=$TEST_ROOT/follows/flakeA/flakeD
+flakeFollowsE=$TEST_ROOT/follows/flakeA/flakeE
+
+# Test following path flakerefs.
+createGitRepo $flakeFollowsA
+mkdir -p $flakeFollowsB
+mkdir -p $flakeFollowsC
+mkdir -p $flakeFollowsD
+mkdir -p $flakeFollowsE
+
+cat > $flakeFollowsA/flake.nix <<EOF
+{
+    description = "Flake A";
+    inputs = {
+        B = {
+            url = "path:./flakeB";
+            inputs.foobar.follows = "foobar";
+        };
+
+        foobar.url = "path:$flakeFollowsA/flakeE";
+    };
+    outputs = { ... }: {};
+}
+EOF
+
+cat > $flakeFollowsB/flake.nix <<EOF
+{
+    description = "Flake B";
+    inputs = {
+        foobar.url = "path:$flakeFollowsA/flakeE";
+        goodoo.follows = "C/goodoo";
+        C = {
+            url = "path:./flakeC";
+            inputs.foobar.follows = "foobar";
+        };
+    };
+    outputs = { ... }: {};
+}
+EOF
+
+cat > $flakeFollowsC/flake.nix <<EOF
+{
+    description = "Flake C";
+    inputs = {
+        foobar.url = "path:$flakeFollowsA/flakeE";
+        goodoo.follows = "foobar";
+    };
+    outputs = { ... }: {};
+}
+EOF
+
+cat > $flakeFollowsD/flake.nix <<EOF
+{
+    description = "Flake D";
+    inputs = {};
+    outputs = { ... }: {};
+}
+EOF
+
+cat > $flakeFollowsE/flake.nix <<EOF
+{
+    description = "Flake E";
+    inputs = {};
+    outputs = { ... }: {};
+}
+EOF
+
+git -C $flakeFollowsA add flake.nix flakeB/flake.nix \
+  flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix
+
+nix flake metadata $flakeFollowsA
+
+nix flake update $flakeFollowsA
+
+nix flake lock $flakeFollowsA
+
+oldLock="$(cat "$flakeFollowsA/flake.lock")"
+
+# Ensure that locking twice doesn't change anything
+
+nix flake lock $flakeFollowsA
+
+newLock="$(cat "$flakeFollowsA/flake.lock")"
+
+diff <(echo "$newLock") <(echo "$oldLock")
+
+[[ $(jq -c .nodes.B.inputs.C $flakeFollowsA/flake.lock) = '"C"' ]]
+[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '["foobar"]' ]]
+[[ $(jq -c .nodes.C.inputs.foobar $flakeFollowsA/flake.lock) = '["B","foobar"]' ]]
+
+# Ensure removing follows from flake.nix removes them from the lockfile
+
+cat > $flakeFollowsA/flake.nix <<EOF
+{
+    description = "Flake A";
+    inputs = {
+        B = {
+            url = "path:./flakeB";
+        };
+        D.url = "path:./flakeD";
+    };
+    outputs = { ... }: {};
+}
+EOF
+
+nix flake lock $flakeFollowsA
+
+[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '"foobar"' ]]
+jq -r -c '.nodes | keys | .[]' $flakeFollowsA/flake.lock | grep "^foobar$"
+
+# Ensure a relative path is not allowed to go outside the store path
+cat > $flakeFollowsA/flake.nix <<EOF
+{
+    description = "Flake A";
+    inputs = {
+        B.url = "path:../flakeB";
+    };
+    outputs = { ... }: {};
+}
+EOF
+
+git -C $flakeFollowsA add flake.nix
+
+nix flake lock $flakeFollowsA 2>&1 | grep 'points outside'
+
+# Non-existant follows should print a warning.
+cat >$flakeFollowsA/flake.nix <<EOF
+{
+    description = "Flake A";
+    inputs.B = {
+        url = "path:./flakeB";
+        inputs.invalid.follows = "D";
+        inputs.invalid2.url = "path:./flakeD";
+    };
+    inputs.D.url = "path:./flakeD";
+    outputs = { ... }: {};
+}
+EOF
+
+git -C $flakeFollowsA add flake.nix
+
+nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'"
+nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'"
diff --git a/tests/flakes/init.sh b/tests/flakes/init.sh
new file mode 100644
index 000000000..36cb9956a
--- /dev/null
+++ b/tests/flakes/init.sh
@@ -0,0 +1,87 @@
+source ./common.sh
+
+requireGit
+
+templatesDir=$TEST_ROOT/templates
+flakeDir=$TEST_ROOT/flake
+nixpkgsDir=$TEST_ROOT/nixpkgs
+
+nix registry add --registry $registry templates git+file://$templatesDir
+nix registry add --registry $registry nixpkgs git+file://$nixpkgsDir
+
+createGitRepo $nixpkgsDir
+createSimpleGitFlake $nixpkgsDir
+
+# Test 'nix flake init'.
+createGitRepo $templatesDir
+
+cat > $templatesDir/flake.nix <<EOF
+{
+  description = "Some templates";
+
+  outputs = { self }: {
+    templates = rec {
+      trivial = {
+        path = ./trivial;
+        description = "A trivial flake";
+        welcomeText = ''
+            Welcome to my trivial flake
+        '';
+      };
+      default = trivial;
+    };
+  };
+}
+EOF
+
+mkdir $templatesDir/trivial
+
+cat > $templatesDir/trivial/flake.nix <<EOF
+{
+  description = "A flake for building Hello World";
+
+  outputs = { self, nixpkgs }: {
+    packages.x86_64-linux = rec {
+      hello = nixpkgs.legacyPackages.x86_64-linux.hello;
+      default = hello;
+    };
+  };
+}
+EOF
+echo a > $templatesDir/trivial/a
+echo b > $templatesDir/trivial/b
+
+git -C $templatesDir add flake.nix trivial/
+git -C $templatesDir commit -m 'Initial'
+
+nix flake check templates
+nix flake show templates
+nix flake show templates --json | jq
+
+createGitRepo $flakeDir
+(cd $flakeDir && nix flake init)
+(cd $flakeDir && nix flake init) # check idempotence
+git -C $flakeDir add flake.nix
+nix flake check $flakeDir
+nix flake show $flakeDir
+nix flake show $flakeDir --json | jq
+git -C $flakeDir commit -a -m 'Initial'
+
+# Test 'nix flake init' with benign conflicts
+createGitRepo "$flakeDir"
+echo a > $flakeDir/a
+(cd $flakeDir && nix flake init) # check idempotence
+
+# Test 'nix flake init' with conflicts
+createGitRepo "$flakeDir"
+echo b > $flakeDir/a
+pushd $flakeDir
+(! nix flake init) |& grep "refusing to overwrite existing file '$flakeDir/a'"
+popd
+git -C $flakeDir commit -a -m 'Changed'
+
+# Test 'nix flake new'.
+rm -rf $flakeDir
+nix flake new -t templates#trivial $flakeDir
+nix flake new -t templates#trivial $flakeDir # check idempotence
+nix flake check $flakeDir
diff --git a/tests/flakes/mercurial.sh b/tests/flakes/mercurial.sh
new file mode 100644
index 000000000..2614006c8
--- /dev/null
+++ b/tests/flakes/mercurial.sh
@@ -0,0 +1,46 @@
+source ./common.sh
+
+if [[ -z $(type -p hg) ]]; then
+    echo "Mercurial not installed; skipping"
+    exit 99
+fi
+
+flake1Dir=$TEST_ROOT/flake-hg1
+mkdir -p $flake1Dir
+writeSimpleFlake $flake1Dir
+hg init $flake1Dir
+
+nix registry add --registry $registry flake1 hg+file://$flake1Dir
+
+flake2Dir=$TEST_ROOT/flake-hg2
+mkdir -p $flake2Dir
+writeDependentFlake $flake2Dir
+hg init $flake2Dir
+
+hg add $flake1Dir/*
+hg commit --config ui.username=foobar@example.org $flake1Dir -m 'Initial commit'
+
+hg add $flake2Dir/flake.nix
+hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Initial commit'
+
+nix build -o $TEST_ROOT/result hg+file://$flake2Dir
+[[ -e $TEST_ROOT/result/hello ]]
+
+(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision)
+
+nix eval hg+file://$flake2Dir#expr
+
+nix eval hg+file://$flake2Dir#expr
+
+(! nix eval hg+file://$flake2Dir#expr --no-allow-dirty)
+
+(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision)
+
+hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Add lock file'
+
+nix flake metadata --json hg+file://$flake2Dir --refresh | jq -e -r .revision
+nix flake metadata --json hg+file://$flake2Dir
+[[ $(nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revCount) = 1 ]]
+
+nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-registries --no-allow-dirty
+nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-use-registries --no-allow-dirty
diff --git a/tests/flakes-run.sh b/tests/flakes/run.sh
similarity index 92%
rename from tests/flakes-run.sh
rename to tests/flakes/run.sh
index 88fc3e628..9fa51d1c7 100644
--- a/tests/flakes-run.sh
+++ b/tests/flakes/run.sh
@@ -1,8 +1,8 @@
-source common.sh
+source ../common.sh
 
 clearStore
 rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
-cp ./shell-hello.nix ./config.nix $TEST_HOME
+cp ../shell-hello.nix ../config.nix $TEST_HOME
 cd $TEST_HOME
 
 cat <<EOF > flake.nix
diff --git a/tests/flake-searching.sh b/tests/flakes/search-root.sh
similarity index 72%
rename from tests/flake-searching.sh
rename to tests/flakes/search-root.sh
index db241f6d2..d8586dc8a 100644
--- a/tests/flake-searching.sh
+++ b/tests/flakes/search-root.sh
@@ -1,15 +1,11 @@
 source common.sh
 
-if [[ -z $(type -p git) ]]; then
-    echo "Git not installed; skipping flake search tests"
-    exit 99
-fi
-
 clearStore
 
-cp ./simple.nix ./simple.builder.sh ./config.nix $TEST_HOME
+writeSimpleFlake $TEST_HOME
 cd $TEST_HOME
 mkdir -p foo/subdir
+
 echo '{ outputs = _: {}; }' > foo/flake.nix
 cat <<EOF > flake.nix
 {
@@ -43,10 +39,12 @@ nix build --override-input foo . || fail "flake should search up directories whe
 sed "s,$PWD/foo,$PWD/foo/subdir,g" -i flake.nix
 ! nix build || fail "flake should not search upwards when part of inputs"
 
-pushd subdir
-git init
-for i in "${success[@]}" "${failure[@]}"; do
-    ! nix build $i || fail "flake should not search past a git repository"
-done
-rm -rf .git
-popd
+if [[ -n $(type -p git) ]]; then
+    pushd subdir
+    git init
+    for i in "${success[@]}" "${failure[@]}"; do
+        ! nix build $i || fail "flake should not search past a git repository"
+    done
+    rm -rf .git
+    popd
+fi
diff --git a/tests/local.mk b/tests/local.mk
index ae15c70f9..dd4924f00 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -1,6 +1,12 @@
 nix_tests = \
-  flakes.sh \
-  flakes-run.sh \
+  flakes/flakes.sh \
+  flakes/run.sh \
+  flakes/mercurial.sh \
+  flakes/circular.sh \
+  flakes/init.sh \
+  flakes/follow-paths.sh \
+  flakes/bundle.sh \
+  flakes/check.sh \
   ca/gc.sh \
   gc.sh \
   remote-store.sh \
@@ -44,7 +50,7 @@ nix_tests = \
   secure-drv-outputs.sh \
   restricted.sh \
   fetchGitSubmodules.sh \
-  flake-searching.sh \
+  flakes/search-root.sh \
   ca/duplicate-realisation-in-closure.sh \
   readfile-context.sh \
   nix-channel.sh \
@@ -80,7 +86,7 @@ nix_tests = \
   nix-copy-ssh.sh \
   post-hook.sh \
   function-trace.sh \
-  flake-local-settings.sh \
+  flakes/config.sh \
   fmt.sh \
   eval-store.sh \
   why-depends.sh \