linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Chapters
  • How it works
  • Lessons
  • Knowledge base
  • Interview prep
home/git/lessons/git-lab-11-1-branching-strategies

lesson ── git-labs ── ~22 мин ── 7 шагов

Trunk-based: features behind flags in main

The goal is to build a trunk-based workflow by hand and see why it looks "flat" compared to GitHub Flow. Each feature lands in main as a small commit behind a flag, then the flag is turned on, then it is removed. No long-lived feature branches.

▶ интерактивный sandbox

Поднимется контейнер gitlab/git-base с git, bash, pre-commit. В браузере откроется терминал, можно сразу git init. Каждый шаг проверяется автоматически. Сеть air-gapped, github.com недоступен.

запустить sandbox →

stack ── git · bash · 256 MB RAM · air-gapped · самоуничтожается через 30 мин простоя

Шаги

  1. 01

    Create a repo with a basic structure

    bash
    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 = {} is a placeholder for the flags you will add as you go. This is a typical pattern.

    ✓ The base is ready. FLAGS is empty.

  2. 02

    Add the first feature behind a flag: one small commit

    The feature is uppercase letters in the greeting. First add it behind a flag that is off: the change lands in main, but the behavior does not change.

    bash
    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 = always create a merge commit, even when a fast-forward is possible
    git merge --no-ff feat/uppercase -m "merge feat/uppercase"
    git branch -d feat/uppercase  # delete it right away: the branch is merged, no longer needed
    python3 app.py                # output is "Hello, world": flag is off

    The key point: the output Hello, world is the same. The feature is in main, but it is off. The branch lived about 30 seconds.

    ✓ The feature is in main, the behavior did not change.

  3. 03

    Turn the flag on in a separate commit

    bash
    cd /home/student/work/trunk-lab
    sed -i 's/"uppercase": False/"uppercase": True/' app.py   # turn the flag on in place
    git commit -am "enable(uppercase): turn on for everyone"
    python3 app.py                # now "HELLO, WORLD"

    The output is now HELLO, WORLD. This is a separate commit: you can roll back only the activation without touching the feature code itself.

    ✓ The flag is on, the behavior changed.

  4. 04

    Add a second feature in parallel, also as a small commit

    Picture two developers working in parallel. The second feature is a custom greeting.

    bash
    cd /home/student/work/trunk-lab
    git switch -c feat/greeting
    # add the key "greeting": False to the FLAGS dict
    sed -i 's/FLAGS = {"uppercase": True}/FLAGS = {"uppercase": True, "greeting": False}/' app.py
    # replace the literal "Hello" with a conditional choice based on the flag
    sed -i 's/out = f"Hello, {name}"/g = "Hi" if FLAGS.get("greeting") else "Hello"\n    out = f"{g}, {name}"/' app.py
    git 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" again: greeting is off

    The output is HELLO, WORLD again: greeting is off. But the feature is already in main.

    ✓ The second feature is merged, the behavior did not change.

  5. 05

    Look at the graph: still almost linear

    bash
    cd /home/student/work/trunk-lab
    git log --oneline --graph --all

    You see 4 commits, two merge commits from --no-ff (this is a convention so the features stay markable). But logically everything moves along main. No parallel long-lived branches.

    ✓ Four commits, a flat structure.

  6. 06

    Turn on the second flag

    bash
    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": both features are active

    The output is HI, WORLD. Both features are active.

    ✓ Both features work.

  7. 07

    Remove the flags: the final step of the lifecycle

    Once the features are stable and there is no reason to turn them off anymore, remove the flags so the code does not accumulate dead code.

    bash
    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

    The output is the same: HI, WORLD. But the code is clean: no FLAGS, no if. The graph is a linear history of the features. This is trunk-based in its final form.

    ✓ The flags are removed, the code is clean. The history is linear.

Что ты узнал

Trunk-based means one eternal branch (main) and short-lived feature branches that live for hours or a day. Features merge behind flags, are turned on separately, and the flags are removed once they run reliably.

команды

  • git switch -c feat/x && ... && git switch main && git merge feat/xa short branch, an hour to a day of life
  • grep -r FLAGS .find every feature flag so you can clean them up

концепции

  • · main is always green: tests pass on every commit
  • · a flag is a condition in the code rather than a real branch
  • · a flag lives exactly until the feature hits GA, then it is removed

← предыдущая

git bisect: find a bug with binary search

следующая →

Fork flow with two remotes

Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies