lesson ── git-labs ── ~20 мин ── 7 шагов
The goal is to learn how to cut a large set of changes into small,
topic-focused commits with git add -p. And to fix a commit you just
made without a new entry in history, using git commit --amend.
интерактивный 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 atomic-lab && cd atomic-lab
git init -b main
cat > server.js <<'EOF'
function add(a, b) {return a + b;
}
console.log(add(2, 3));
EOF
git add . && git commit -m "init: add function"
git log --oneline
The starting point is one file with one function.
✓ Starting commit is in place.
Imagine a typical workday: you tweaked the typing, added a new feature, fixed a bug, all in one file.
cd /home/student/work/atomic-lab
cat > server.js <<'EOF'
// refactor: jsdoc types
/**
* @param {number} a * @param {number} b*/
function add(a, b) {if (a == null || b == null) return 0;
return a + b;
}
// feat: subtract
function sub(a, b) {return a - b;
}
console.log(add(2, 3));
console.log(sub(5, 2));
EOF
git diff
Three topics are mixed together: refactor (jsdoc), fix (null-check), feat (sub). In normal work you would just commit this as "misc changes". Now you will split them.
✓ Three topics in one diff. Next you will cut them apart.
Since the file is already changed, practice amend on the message separately. First, make the working tree clean again:
cd /home/student/work/atomic-lab
git stash push -m "wip-mess" # set uncommitted work aside in the stash with a label
git log --oneline
# --amend replaces the last commit with a new one (with a different SHA), -m sets the new message
git commit --amend -m "init: add function with two-arg signature"
git log --oneline # the commit's SHA changed
git stash pop # bring the stashed work back
Notice: the message changed, but the commit's SHA became a
different one (check with git log -1 --format=%H). amend is not an
"edit in place"; it creates a new commit and moves the branch
pointer.
✓ Message fixed with amend. Next, add -p.
git add -p server.js starts an interactive dialog. For each hunk
Git asks: Stage this hunk [y,n,q,a,d,s,e,?]?.
The most useful answers:
y is yes, into the indexn is no, leave it in the working trees is split (cut the hunk into smaller ones)q is quitcd /home/student/work/atomic-lab
git add -p server.js # -p (--patch) = interactive staging by hunk
Answer y on the hunk with the jsdoc types and null-check
refactor (use s if they are stuck together), n on everything
that touches sub. After that, check:
git diff --cached # what went into the index (ready to commit)
git diff # what is left unstaged in the working tree
Only the refactor should land in the index. Subtract stayed in the working tree.
If you get lost in the interactive prompt, `q` exits. The run is non-destructive, so you can repeat it.
✓ Refactor in the index, feature still in the working tree.
cd /home/student/work/atomic-lab
git commit -m "refactor: add jsdoc types and null-check"
git log --oneline
git status
Status shows that server.js is still modified: subtract is in the working tree.
✓ First atomic commit is ready.
Now add the rest, which is only feat:
cd /home/student/work/atomic-lab
git add server.js
git diff --cached
git commit -m "feat: add sub function"
git log --oneline
Three commits: init, refactor, feat. Each one is its own topic.
✓ Atomic decomposition is done.
Noticed you forgot to add console.log(sub(...)) as a separate
commit? It is part of the same feature, so you can fold it into an amend.
But console.log is already in the file. Simulate "forgot and added it":
cd /home/student/work/atomic-lab
echo "// usage example below" > note.txt
git add note.txt
# --amend = replace HEAD with a new commit, --no-edit = keep the old message
git commit --amend --no-edit
git log --oneline # the commit count is the same, but HEAD now contains note.txt
git show HEAD --stat # --stat = short summary of changed files
--amend --no-edit added the file to the last commit and kept the
message. History is still 3 commits, but the last one now contains
note.txt.
Rule: amend only before you push to a shared branch. On a pushed
branch this means a force-push, which breaks git pull for everyone else.
✓ Amend added to the commit. History stayed short.
git add -p is interactive staging by hunk. You can build a commit
from part of the changes in a file. git commit --amend rewrites the
last commit: either the message or the content.
команды
git add -p fileinteractively pick hunks for staginggit diff --cachedcheck what went into the index before the commitgit commit --amendfix the last commit (message or content)git commit --amend --no-editkeep the message, add a new stageконцепции