lesson ── git-labs ── ~22 мин ── 8 шагов
The goal is to take a feature branch through its full cycle. You create it from main, make a couple of commits, see a fast-forward merge and a three-way merge, try to delete an unmerged branch, and get an honest refusal from Git.
интерактивный 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 branches-lab && cd branches-lab
git init -b main
echo "base" > readme.txt
git add . && git commit -m "init"
git branch
There is one branch right now, main. Next you create your own.
✓ A repo with one commit on main. Ready to branch.
cd /home/student/work/branches-lab
git switch -c feat/quick # -c = create, same as `checkout -b`
git branch # an asterisk marks the active branch
git log --oneline --graph --all # --graph = ASCII graph, --all = all refs
switch -c (== checkout -b) creates a branch from HEAD and switches
to it. git branch now shows both, with an asterisk on the active one.
If you get `unknown switch 'switch'`, the git is old. Use `checkout -b feat/quick`.
✓ The branch is created, and you are on it.
cd /home/student/work/branches-lab
echo "quick feature" > quick.txt
git add . && git commit -m "feat: add quick"
git log --oneline --graph --all
feat/quick now has 2 commits, main has 1. The graph does not show any divergence yet, because main has not branched off.
✓ The branch moved one commit ahead.
cd /home/student/work/branches-lab
git switch main
git merge feat/quick # ff = just move the ref forward, no merge commit
git log --oneline --graph --all
The message is Fast-forward. main and feat/quick now point to the same
commit. There is no merge commit: main just slid forward, because main
and feat/quick had not diverged.
✓ Fast-forward done. The graph stayed linear.
For a three-way merge, both branches have to diverge from a common ancestor.
cd /home/student/work/branches-lab
git switch -c feat/slow
echo "slow line" > slow.txt
git add . && git commit -m "feat: add slow"
git switch main
echo "main update" >> readme.txt # >> = append, does not overwrite
git commit -am "main: update readme" # -a = stage already-tracked files, -m = message
git log --oneline --graph --all
Now main and feat/slow have diverged. The graph shows a V shape.
✓ The branches have diverged. Ready for the three-way.
cd /home/student/work/branches-lab
# --no-edit = take the default merge-commit message, do not open the editor
git merge --no-edit feat/slow
git log --oneline --graph --all
The message is Merge made by the 'ort' strategy. A merge commit with
two parents appears in the log. The graph is no longer linear.
Files from both branches end up in the working tree. They touched different files, so there is no conflict.
✓ The merge commit is created. That is the three-way.
cd /home/student/work/branches-lab
git branch -d feat/quick # -d = "delete if merged", safe
git branch -d feat/slow
git branch
Both delete without complaint, since they are merged into main.
branch -d checks this itself and refuses when they are not.
✓ The merged branches are gone. Only main remains.
cd /home/student/work/branches-lab
git switch -c feat/draft
echo "draft idea" > draft.txt
git add . && git commit -m "draft"
git switch main
git branch -d feat/draft # fails: the branch is not merged into main
You should get the error: error: The branch 'feat/draft' is not fully merged.. This is Git's protection: you could lose commits by deleting a
branch that is not in main.
To force the delete, use a capital -D:
git branch -D feat/draft # -D (capital) = forced delete
git branch
Now it really is deleted. If the commit is used nowhere else, git gc
sweeps it up when it runs (by hand or as gc --auto). The reflog keeps a
record of the branch for about 30 days by default, after which the commit
becomes a candidate for collection.
✓ You saw Git's protection, and the forced delete worked.
A branch is a pointer to a commit. You create it with git branch or
switch -c, and switch with switch. A merge can be fast-forward or
three-way, depending on whether the target moved. You delete with
branch -d (safe) or -D (forced).
команды
git switch -c feat/xcreate and switchgit merge feat/xmerge feat/x into the current branch (ff or 3-way)git merge --no-ff feat/xforce a merge commit even when ff is possiblegit branch -d feat/xdelete if the branch is mergedgit branch -D feat/xdelete by forceконцепции