aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs')
-rw-r--r--pkgs/populate/default.nix95
1 files changed, 93 insertions, 2 deletions
diff --git a/pkgs/populate/default.nix b/pkgs/populate/default.nix
index c707b7c..7130570 100644
--- a/pkgs/populate/default.nix
+++ b/pkgs/populate/default.nix
@@ -142,6 +142,68 @@ let
fi
'';
+ # Shell code printing why populate can't use the .git directory of the
+ # Git work tree at path, or nothing. A .git directory that git clone and
+ # populate made has no symlinks, no commondir file, which names another
+ # repository, no linked work trees, and only the configuration they write:
+ # the repository's format, the remote, branches, and submodules, whose
+ # repositories have their work tree in the work tree.
+ gitProblems = path: let
+ gitDir = "${path}/.git";
+ # Keys of the configuration git clone writes, as patterns of case.
+ keys = [
+ "core.repositoryformatversion" "core.filemode" "core.bare"
+ "core.logallrefupdates" "core.symlinks" "core.ignorecase"
+ "core.precomposeunicode"
+ "extensions.objectformat" "extensions.refstorage"
+ "remote.origin.url" "remote.origin.fetch"
+ "branch.*.remote" "branch.*.merge"
+ "submodule.active" "submodule.*.url" "submodule.*.active"
+ ];
+ in /* sh */ ''(
+ links=$(find ${quote gitDir} -type l | head -n 3 | paste -s -d ' ' -)
+ if test -n "$links"; then
+ printf '%s contains symlinks: %s' ${quote gitDir} "$links"
+ exit
+ fi
+ common=$(find ${quote gitDir} -name commondir | head -n 1)
+ if test -n "$common"; then
+ printf '%s names another repository' "$common"
+ exit
+ fi
+ if test -e ${quote gitDir}/worktrees; then
+ printf '%s has linked work trees' ${quote gitDir}
+ exit
+ fi
+ find ${quote gitDir} -name config -type f | while IFS= read -r config; do
+ command git config --file "$config" --list --name-only |
+ while IFS= read -r key; do
+ case $key in
+ ${concatStringsSep "|" keys}) ;;
+ core.worktree)
+ # Submodules' repositories have their work tree in the work tree.
+ worktree=$(command git config --file "$config" core.worktree)
+ dir=$(dirname "$config")
+ case $dir/$worktree in
+ ${quote gitDir}/modules/*)
+ resolved=$(cd "$dir" && cd "$worktree" 2>/dev/null && pwd -P) || :
+ case $resolved in
+ "$(cd ${quote path} && pwd -P)"/*) continue ;;
+ esac
+ ;;
+ esac
+ printf '%s sets core.worktree to %s' "$config" "$worktree"
+ exit 1
+ ;;
+ *)
+ printf '%s sets %s, which neither git clone nor populate does' "$config" "$key"
+ exit 1
+ ;;
+ esac
+ done || exit 1
+ done || :
+ )'';
+
check = { force, target }: let
sentinelFile = "${target.path}/.populate";
in runShell target /* sh */ ''
@@ -1000,6 +1062,12 @@ let
pop.git = target: source: runShell target /* sh */ ''
set -efu
+ # Hooks and commands a repository's configuration names run with Git's
+ # commands, so populate's Git doesn't run any. The configuration gets
+ # checked as well, but Git also runs a hook it has itself.
+ git() {
+ command git -c core.hooksPath=/dev/null -c core.fsmonitor=false "$@"
+ }
# Remove target path if it doesn't look like a git worktree.
# This can happen e.g. when it had a different type earlier. A work
# tree that a symlink points to, or whose .git is a symlink or a file
@@ -1010,6 +1078,27 @@ let
! test -d ${quote target.path}/.git; then
rm -fR ${quote target.path}
fi
+ # A .git directory can name places elsewhere too, which Git's commands
+ # change, or commands, which they run. Populate only uses a work tree
+ # whose .git directory has nothing like that, and else replaces it if
+ # reclone is set, or fails, telling why.
+ if test -e ${quote target.path}; then
+ # Not $((, which would be arithmetic.
+ if problems=$( ${gitProblems target.path} ); then :; else
+ exit 1
+ fi
+ if test -n "$problems"; then
+ ${if source.reclone then /* sh */ ''
+ >&2 printf 'replacing the Git work tree %s, because %s\n' \
+ ${quote target.path} "$problems"
+ rm -fR ${quote target.path}
+ '' else /* sh */ ''
+ >&2 printf 'error: populate doesn'"'"'t use the Git work tree %s, because %s. Fix that, remove the work tree, or set the source'"'"'s reclone to true to replace it by a fresh clone.\n' \
+ ${quote target.path} "$problems"
+ exit 1
+ ''}
+ fi
+ fi
if ! test -e ${quote target.path}; then
${if source.shallow then /* sh */ ''
git init ${quote target.path}
@@ -1043,9 +1132,11 @@ let
''}
fi
''}
- git reset --hard "$hash" >&2
- git submodule update --init --recursive
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.
+ git reset --hard "$hash" >&2
+ git submodule update --init --recursive
git clean -dfx \
${concatMapStringsSep " "