From 882c22d42a7b2dac38272986ac732cb742548723 Mon Sep 17 00:00:00 2001 From: mt Date: Mon, 15 Jun 2026 20:30:07 +0200 Subject: [PATCH] Add Git LFS locking setup: policy, git-sync, onboarding --- .githooks/git-sync.cmd | 34 +++++++++++++++++++++ .githooks/git-sync.sh | 36 ++++++++++++++++++++++ ProjectSettings/GitLocksConfig.json | 18 +++++++++++ README.md | 23 ++++++++++++++ setup-locks.sh | 47 +++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 .githooks/git-sync.cmd create mode 100755 .githooks/git-sync.sh create mode 100644 ProjectSettings/GitLocksConfig.json create mode 100644 setup-locks.sh diff --git a/.githooks/git-sync.cmd b/.githooks/git-sync.cmd new file mode 100644 index 0000000..41bdf9d --- /dev/null +++ b/.githooks/git-sync.cmd @@ -0,0 +1,34 @@ +@echo off +rem git-sync (Windows): push the current branch, then release my LFS locks on fully pushed files. +rem Mirrors the "Push & release my locks" button in the Unity Git Locks window. +setlocal enabledelayedexpansion + +for /f "delims=" %%r in ('git rev-parse --show-toplevel') do cd /d "%%r" +for /f "delims=" %%b in ('git rev-parse --abbrev-ref HEAD') do set BRANCH=%%b + +echo Pushing !BRANCH!... +git push origin HEAD +if errorlevel 1 ( + echo Push failed, no lock released. + exit /b 1 +) + +for /f "delims=" %%m in ('git config --get wayLocks.username 2^>nul') do set ME=%%m + +echo Releasing locks on fully pushed files... +for /f "tokens=1,2 delims= " %%p in ('git lfs locks 2^>nul') do ( + set "P=%%p" + set "O=%%q" + if defined ME if not "!O!"=="!ME!" ( + rem skip locks owned by others + ) else ( + git status --porcelain -- "!P!" > "%TEMP%\gitlocks_status.txt" + for %%z in ("%TEMP%\gitlocks_status.txt") do if %%~zz==0 ( + git lfs unlock "!P!" >nul 2>&1 && echo released !P! + ) + ) +) +del "%TEMP%\gitlocks_status.txt" 2>nul + +echo Done. +endlocal diff --git a/.githooks/git-sync.sh b/.githooks/git-sync.sh new file mode 100755 index 0000000..bd4a801 --- /dev/null +++ b/.githooks/git-sync.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env sh +# git-sync: push the current branch, then release my LFS locks on files that are fully pushed. +# Mirrors the "Push & release my locks" button in the Unity Git Locks window. +# +# Installed as `git sync` by setup-git-locks.sh. Run it from anywhere inside the repo. +# +# A lock is released only if its file has a clean working tree (nothing uncommitted), +# i.e. the version on the server is the one you finished. Files you still have changes +# on keep their lock. Locks owned by other people are skipped (and the server enforces it anyway). + +set -e + +cd "$(git rev-parse --show-toplevel)" +branch=$(git rev-parse --abbrev-ref HEAD) + +printf 'Pushing %s...\n' "$branch" +git push origin HEAD + +# Optional: only touch my own locks if a username is configured (matches the LFS lock owner). +me=$(git config --get wayLocks.username || true) + +printf 'Releasing locks on fully pushed files...\n' +tab=$(printf '\t') +git lfs locks 2>/dev/null | while IFS="$tab" read -r path owner rest; do + [ -z "$path" ] && continue + if [ -n "$me" ] && [ "$owner" != "$me" ]; then + continue + fi + if [ -z "$(git status --porcelain -- "$path")" ]; then + if git lfs unlock "$path" >/dev/null 2>&1; then + printf ' released %s\n' "$path" + fi + fi +done + +printf 'Done.\n' diff --git a/ProjectSettings/GitLocksConfig.json b/ProjectSettings/GitLocksConfig.json new file mode 100644 index 0000000..bb74e26 --- /dev/null +++ b/ProjectSettings/GitLocksConfig.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "mode": 0, + "autoUnlockOnPush": true, + "lockMetaWithAsset": false, + "rules": [ + { + "name": "Scenes & serialized assets", + "extensions": ".unity, .prefab, .asset, .controller, .anim, .mat, .playable, .spriteatlas, .mixer", + "trigger": 1 + }, + { + "name": "Heavy binaries (art/audio)", + "extensions": ".fbx, .obj, .blend, .psd, .png, .tga, .tif, .tiff, .exr, .wav, .mp3, .ogg, .aif, .aiff", + "trigger": 2 + } + ] +} diff --git a/README.md b/README.md index a36ae29..bcfc8c7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ # Modern Unity Template Includes .gitignore and .gitattributes, with LFS support. + +## Git LFS file locking + +This template is ready for Perforce-style file locking with the +[unity-git-locks (WAY fork)](https://github.com/wayexperience/unity-git-locks) plugin. + +After cloning a project based on this template: + +```sh +sh setup-locks.sh soft +``` + +That installs LFS, the `UnityYAMLMerge` driver, and a `git sync` alias +(push + release your finished locks). The shared lock policy is committed in +`ProjectSettings/GitLocksConfig.json`. + +Then in Unity: add the plugin via Package Manager +(`https://github.com/wayexperience/unity-git-locks.git#v1.3.0`), set +**Asset Serialization = Force Text** and **Version Control = Visible Meta Files**, +and put your username under **Preferences > Git Locks**. + +Soft mode (advisory locks, recommended) keeps files editable and only blocks the +push on files locked by others. Use `setup-locks.sh hard` for read-only enforcement. diff --git a/setup-locks.sh b/setup-locks.sh new file mode 100644 index 0000000..4867205 --- /dev/null +++ b/setup-locks.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env sh +# Configure this repo for Git LFS file locking with the WAY auto-lock plugin. +# Run once per clone, from anywhere inside the repo: +# sh setup-locks.sh [soft|hard] [forgejo-username] + +set -e + +MODE="${1:-soft}" +USERNAME="${2:-}" + +cd "$(git rev-parse --show-toplevel)" + +git lfs install + +if [ "$MODE" = "hard" ]; then + git config lfs.setlockablereadonly true +else + git config lfs.setlockablereadonly false +fi +echo "lfs.setlockablereadonly = $(git config --get lfs.setlockablereadonly)" + +# UnityYAMLMerge smart-merge driver (the .gitattributes already references merge=unityyamlmerge). +# Adjust the path to your Unity install if 'UnityYAMLMerge' is not on PATH. +git config merge.unityyamlmerge.name "Unity SmartMerge" +git config merge.unityyamlmerge.driver 'UnityYAMLMerge merge -p %O %B %A %A' +git config merge.unityyamlmerge.recursive binary + +# 'git sync' = push current branch + release my finished locks (mirrors the plugin's button). +chmod +x "$(git rev-parse --show-toplevel)/.githooks/git-sync.sh" 2>/dev/null || true +git config alias.sync '!sh "$(git rev-parse --show-toplevel)/.githooks/git-sync.sh"' + +if [ -n "$USERNAME" ]; then + git config wayLocks.username "$USERNAME" + echo "wayLocks.username = $USERNAME" +else + echo "Set your Forgejo username: git config wayLocks.username " +fi + +cat <<'EOF' + +Done (git side). In Unity: + 1. Package Manager > + > Add package from git URL: + https://github.com/wayexperience/unity-git-locks.git#v1.3.0 + 2. Project Settings > Editor: Asset Serialization = Force Text, Version Control = Visible Meta Files. + 3. Preferences > Git Locks: set your username (same as wayLocks.username). + The lock policy is already committed in ProjectSettings/GitLocksConfig.json. +EOF