aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-13 14:01:19 +0000
committertv <tv@krebsco.de>2026-11-13 15:06:32 +0100
commit36cfd6b9fa8d729172f90e8fdd507349905d4f11 (patch)
tree93424f012ce3b538eab7550429cc654331476d25
parent494b3127d82ea8139a6937a1064ea58f62bfe4bf (diff)
populate: follow branches and tags of git sources
A ref that consists of 4 to 40 hex digits is a commit, which populate fetches only if the work tree lacks it, unless fetchAlways is set. Any other ref is a branch or tag, which populate fetches on every run, resetting the work tree to the commit fetched. Resetting to the name itself resolves to a local branch, which fetching doesn't move.
-rw-r--r--CHANGELOG.md6
-rw-r--r--README.md6
-rw-r--r--pkgs/populate/default.nix41
-rw-r--r--tests/scenarios.nix34
4 files changed, 69 insertions, 18 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d120e39..78721ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changes
+## Unreleased
+
+- `git` sources with a branch or tag as `ref` follow it: populate fetches
+ the branch or tag on every run, and resets the work tree to what it
+ fetched.
+
## 1.31.0
- New source types `flake` and `flake-input` deliver a flake, or one input
diff --git a/README.md b/README.md
index d88f4e1..2784a9d 100644
--- a/README.md
+++ b/README.md
@@ -560,7 +560,11 @@ Supported attributes:
URL of the Git repository that should be fetched.
* `ref` -
- Branch / tag / commit that should be fetched.
+ Branch / tag / commit that should be fetched. A branch or tag gets fetched
+ on every run, so the work tree follows it. A commit, given by its hash or a
+ prefix thereof of at least four hex digits, only gets fetched if the work
+ tree lacks it, unless `fetchAlways` is set. So a branch or tag whose name
+ consists of hex digits only counts as a commit.
* `clean.exclude` -
List of patterns that should be excluded from Git cleaning.
diff --git a/pkgs/populate/default.nix b/pkgs/populate/default.nix
index a5daee8..1787b65 100644
--- a/pkgs/populate/default.nix
+++ b/pkgs/populate/default.nix
@@ -1209,25 +1209,32 @@ let
git remote set-url origin ${quote source.url}
fi
- # TODO resolve git_ref to commit hash
- hash=${quote source.ref}
-
- if ! test "$(git log --format=%H -1)" = "$hash"; then
- ${if source.fetchAlways then /* sh */ ''
- ${if source.shallow then /* sh */ ''
- git fetch --depth=1 origin "$hash"
- '' else /* sh */ ''
- git fetch origin
- ''}
+ # A commit, given by its hash or a prefix thereof, gets fetched only if
+ # the work tree lacks it, unless fetchAlways is set. A branch or tag
+ # gets fetched on every run, so the work tree follows it: resetting to
+ # its name would reset to a local branch, which fetching doesn't move.
+ ref=${quote source.ref}
+ fetch() {
+ ${if source.shallow then /* sh */ ''
+ git fetch --depth=1 origin "$1"
'' else /* sh */ ''
- if ! git log -1 "$hash" >/dev/null 2>&1; then
- ${if source.shallow then /* sh */ ''
- git fetch --depth=1 origin "$hash"
- '' else /* sh */ ''
- git fetch origin
- ''}
- fi
+ git fetch origin "$1"
''}
+ }
+ if printf %s "$ref" | grep -Eqx '[0-9a-f]{4,40}'; then
+ hash=$ref
+ if ! test "$(git log --format=%H -1)" = "$hash"; then
+ ${if source.fetchAlways then /* sh */ ''
+ ${if source.shallow then ''fetch "$hash"'' else "git fetch origin"}
+ '' else /* sh */ ''
+ if ! git log -1 "$hash" >/dev/null 2>&1; then
+ ${if source.shallow then ''fetch "$hash"'' else "git fetch origin"}
+ fi
+ ''}
+ fi
+ else
+ fetch "$ref"
+ hash=$(git rev-parse --verify 'FETCH_HEAD^{commit}')
fi
# Also if HEAD is there already: rewriting a lock file in place, e.g.,
# changes the work tree, and stages the lock file if Nix created it.
diff --git a/tests/scenarios.nix b/tests/scenarios.nix
index 4e869dc..e618fcd 100644
--- a/tests/scenarios.nix
+++ b/tests/scenarios.nix
@@ -595,6 +595,40 @@ assert !(krops.isLocalTarget (targets.remote "/"));
}
];
})) targetKinds
+ # Branches and tags follow the server: a branch that moves between two
+ # deploys, and a tag, with full and shallow clones.
+ ++ concatMap (targetKind: map (shallow: scenario {
+ name = "git-${targetKind}-shallow${if shallow then "1" else "0"}-refs";
+ inherit targetKind;
+ prepareDeployer = ''
+ git -C /fixtures/git/gitsrc.git update-ref refs/heads/moving ${gitFirst}
+ git -C /fixtures/git/gitsrc.git update-ref refs/tags/first ${gitFirst}
+ '';
+ phases = [
+ {
+ sources.x.git = { url = gitUrl; ref = "moving"; inherit shallow; };
+ check = ''
+ expect_eq HEAD "$(git -C "$T"/x rev-parse HEAD)" ${gitFirst}
+ '';
+ mutateDeployer = ''
+ git -C /fixtures/git/gitsrc.git update-ref refs/heads/moving ${gitHead}
+ '';
+ }
+ {
+ sources.x.git = { url = gitUrl; ref = "moving"; inherit shallow; };
+ check = ''
+ expect_eq HEAD "$(git -C "$T"/x rev-parse HEAD)" ${gitHead}
+ '';
+ }
+ {
+ sources.x.git = { url = gitUrl; ref = "first"; inherit shallow; };
+ check = ''
+ expect_eq HEAD "$(git -C "$T"/x rev-parse HEAD)" ${gitFirst}
+ '';
+ }
+ ];
+ }) [ false true ]) targetKinds
+
# A git source stops drawing inputs: its lock file, which Nix created and
# staged, goes, so the work tree is as the commit has it.
++ map (targetKind: scenario {