lesson ── git-labs ── ~22 мин ── 7 шагов
Цель - руками собрать trunk-based workflow и увидеть, почему он выглядит «плоско» по сравнению с GitHub Flow. Каждая фича уезжает в main маленьким коммитом за флагом, потом флаг включается, потом удаляется. Никаких долгоживущих feature-веток.
интерактивный sandbox
Поднимется контейнер gitlab/git-base с git, bash, pre-commit. В браузере откроется терминал, можно сразу git init. Каждый шаг проверяется автоматически. Сеть air-gapped, github.com недоступен.
stack ── git · bash · 256 MB RAM · air-gapped · самоуничтожается через 30 мин простоя
cd /home/student/work
mkdir -p trunk-lab && cd trunk-lab
git init -b main
cat > app.py <<'EOF'
FLAGS = {}def greet(name):
return f"Hello, {name}"print(greet("world"))EOF
git add . && git commit -m "init: greet"
FLAGS = {} - заглушка для флагов, которые будут добавляться по
ходу. Это типичный паттерн.
✓ База готова. FLAGS пустой.
Фича - заглавные буквы в приветствии. Сначала добавь её под выключенным флагом - изменения попадают в main, но поведение не меняется.
cd /home/student/work/trunk-lab
git switch -c feat/uppercase
cat > app.py <<'EOF'
FLAGS = {"uppercase": False}def greet(name):
out = f"Hello, {name}" if FLAGS.get("uppercase"):out = out.upper()
return out
print(greet("world"))EOF
git add . && git commit -m "feat(uppercase): scaffold behind flag, default off"
git switch main
# --no-ff = всегда создать merge-коммит, даже если возможен fast-forward
git merge --no-ff feat/uppercase -m "merge feat/uppercase"
git branch -d feat/uppercase # сразу удаляем - ветка слита, не нужна
python3 app.py # вывод "Hello, world" - флаг off
Главное: вывод Hello, world - тот же. Фича в main, но
выключена. Ветка прожила ~30 секунд.
✓ Фича в main, поведение не изменилось.
cd /home/student/work/trunk-lab
sed -i 's/"uppercase": False/"uppercase": True/' app.py # включаем флаг in-place
git commit -am "enable(uppercase): turn on for everyone"
python3 app.py # теперь "HELLO, WORLD"
Вывод теперь HELLO, WORLD. Это отдельный коммит - можно
откатить только включение, не трогая сам код фичи.
✓ Флаг включён, поведение изменилось.
Имитируем работу двух разработчиков параллельно. Вторая фича - кастомное приветствие.
cd /home/student/work/trunk-lab
git switch -c feat/greeting
# добавляем ключ "greeting": False в словарь FLAGS
sed -i 's/FLAGS = {"uppercase": True}/FLAGS = {"uppercase": True, "greeting": False}/' app.py# заменяем литеральное "Hello" на условный выбор в зависимости от флага
sed -i 's/out = f"Hello, {name}"/g = "Hi" if FLAGS.get("greeting") else "Hello"\n out = f"{g}, {name}"/' app.pygit commit -am "feat(greeting): customizable, behind flag"
git switch main
git merge --no-ff feat/greeting -m "merge feat/greeting"
git branch -d feat/greeting
python3 app.py # снова "HELLO, WORLD" - greeting off
Вывод снова HELLO, WORLD - greeting выключен. Но фича уже в main.
✓ Вторая фича смержена, поведение не изменилось.
cd /home/student/work/trunk-lab
git log --oneline --graph --all
Видишь 4 коммита, два merge-коммита от --no-ff (это
pre-конвенция, чтобы фичи были маркируемы). Но логически - всё
идёт по main. Никаких параллельных долгоживущих веток.
✓ Четыре коммита, плоская структура.
cd /home/student/work/trunk-lab
sed -i 's/"greeting": False/"greeting": True/' app.py
git commit -am "enable(greeting): GA"
python3 app.py # "HI, WORLD" - обе фичи активны
Вывод HI, WORLD. Обе фичи активны.
✓ Обе фичи работают.
Когда фичи стабильны и больше нет смысла их отключать - удали флаги, чтобы код не накапливал dead code.
cd /home/student/work/trunk-lab
cat > app.py <<'EOF'
def greet(name):
return f"Hi, {name}".upper()print(greet("world"))EOF
git commit -am "cleanup: remove uppercase + greeting flags (both GA)"
python3 app.py
git log --oneline --graph
Вывод тот же - HI, WORLD. Но код чистый: ни FLAGS, ни if.
Граф - линейная история фич. Это и есть trunk-based в финале.
✓ Флаги удалены, код чистый. История - линейная.
Trunk-based - одна вечная ветка (main) и короткоживущие feature-ветки на часы-день. Фичи мерджатся под флагами, включаются отдельно, флаги удаляются после стабильной работы.
команды
git switch -c feat/x && ... && git switch main && git merge feat/xкороткая ветка, час-день жизниgrep -r FLAGS .найти все feature flags, чтобы вычиститьконцепции