Junior: питання на співбесіді з теми «Історія й гілки»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
5 питань
Обидві команди переносять зміни з однієї гілки в іншу, але по-різному формують історію.
git merge main (у своїй гілці) створює коміт злиття з двома батьками. Історія показує, як було насправді: дві гілки розвивалися паралельно і зійшлися.
git rebase main переписує коміти гілки так, ніби вона почалася з останнього коміту main: кожен коміт застосовується заново й отримує новий хеш. Історія стає лінійною, без комітів злиття.
git switch feature
git rebase main # перебудувати feature поверх свіжого main
git switch main
git merge --ff-only feature # тепер злиття - просте перемотування
Що обирати:
- Rebase - для власної гілки, щоб підтягнути свіжий
mainі отримати чисту історію перед pull request. - Merge - для злиття спільних гілок і коли важливо зберегти, як саме йшла робота.
Головне правило rebase: не переписувати коміти, які вже опубліковані й використовуються іншими. Після rebase гілку доведеться відправляти з --force-with-lease, а в колег, що працювали поверх старих комітів, історія розійдеться.
Конфлікти бувають і там, і там. Але при rebase їх розв'язують по коміту: якщо гілка з 10 комітів зачіпає те саме місце, конфлікт може з'явитися кілька разів.
Спосіб залежить від того, чи коміт уже відправлено в спільний репозиторій.
Коміт ще локальний - git reset пересуває гілку назад:
git reset --soft HEAD~1 # коміт скасовано, зміни лишилися в індексі (staged)
git reset HEAD~1 # (--mixed) зміни лишилися у файлах, але не staged
git reset --hard HEAD~1 # коміт і зміни видалено повністю
Найчастіше потрібен --soft чи --mixed: виправити повідомлення, додати забутий файл, розбити коміт. --hard знищує незбережену роботу.
Виправити лише останній коміт - git commit --amend: змінити повідомлення чи додати файли.
Коміт уже в спільній гілці - git revert створює новий коміт, що скасовує зміни старого. Історія не переписується, тож колегам нічого не ламається:
git revert a1b2c3d
git push
Головна відмінність: reset переписує історію (коміт ніби зник), revert додає до неї (коміт є, і є його скасування). Для main, з якого вже розгорнуто прод, - лише revert.
Для окремих файлів є git restore: git restore app/User.php - відкинути незакомічені зміни у файлі, git restore --staged app/User.php - прибрати з індексу.
git log без параметрів виводить довгий список комітів, у якому важко зорієнтуватися. Кілька параметрів роблять його зручним інструментом.
Компактний вигляд і граф гілок:
git log --oneline # хеш і заголовок в один рядок
git log --oneline --graph --all # граф усіх гілок
git log --oneline -10 # лише останні 10
Фільтри:
| Параметр | Що показує |
|---|---|
--author="Olena" |
коміти автора |
--since="2 weeks ago" |
за період (--until - до дати) |
--grep="invoice" |
коміти, в повідомленні яких є слово |
-- app/Models/User.php |
коміти, що змінювали файл |
--follow -- file.php |
історія файлу з урахуванням перейменувань |
main..feature |
коміти гілки feature, яких ще немає в main |
-S "calculateTax" |
коміти, що додали чи видалили цей рядок коду |
Зміни разом з історією:
git log -p -- app/Services/Billing.php # diff кожного коміту
git log --stat # які файли й скільки рядків змінено
git show - один об'єкт детально:
git show a1b2c3d # повідомлення коміту й diff
git show HEAD~2 --stat # коміт, на два раніше від поточного
git show main:config/app.php # вміст файлу в гілці main, не перемикаючись
Власний формат:
git log --pretty=format:"%h %ad %an %s" --date=short
Практичні поради:
-S(пошук «кирками», pickaxe) - найшвидший спосіб з'ясувати, коли з'явилася чи зникла функція;main..featureперед злиттям показує, що саме потрапить уmain;- часто вживані варіанти варто оформити як аліас:
git config --global alias.lg "log --oneline --graph --all".
git commit --amend замінює останній коміт новим: з виправленим повідомленням, доданими файлами чи обома змінами.
Виправити повідомлення:
git commit --amend -m "Fix invoice total rounding"
Додати забутий файл:
git add tests/Feature/InvoiceTest.php
git commit --amend --no-edit # зберегти попереднє повідомлення
Змінити автора (наприклад, коміт зроблено з неправильною поштою):
git commit --amend --reset-author --no-edit
Що відбувається насправді: Git не змінює старий коміт - коміти незмінні. Він створює новий коміт з іншим хешем і переставляє на нього гілку. Старий коміт лишається в репозиторії (його видно в git reflog), доки його не прибере збирач сміття.
Коли --amend безпечний: коміт ще не відправлено у спільний репозиторій (git push) або гілка особиста й ніхто на неї не спирається.
Коли не можна: коміт уже в спільній гілці (main, гілка колеги). Після --amend локальна історія розходиться з віддаленою:
! [rejected] main -> main (non-fast-forward)
Щоб відправити, доведеться робити force push - і колеги, що вже отримали старий коміт, матимуть конфлікти й дублікати. У спільній гілці замість --amend - новий коміт з виправленням.
У своїй гілці pull request після --amend використовуйте git push --force-with-lease - він не перезапише чужих змін, якщо вони з'явилися на сервері.
Виправити не останній, а давніший коміт - через git commit --fixup <хеш> і git rebase -i --autosquash, а не --amend.
Якщо --amend зроблено помилково: git reset --soft HEAD@{1} повертає гілку на попередній коміт, а зміни лишаються в індексі.
git cherry-pick бере зміни з указаного коміту й застосовує їх як новий коміт у поточній гілці.
git switch release/2.4
git cherry-pick a1b2c3d
Новий коміт має той самий diff і повідомлення, але інший хеш: в нього інший батько й час створення.
Типові сценарії:
- виправлення в реліз: баг виправлено в
main, і те саме виправлення потрібне в гілці підтримуваної версії; - коміт не в тій гілці: зроблено коміт у
mainзамість гілки задачі - перенести його в потрібну гілку, а зmainприбрати; - врятувати частину роботи з гілки, яку не будуть зливати.
Кілька комітів:
git cherry-pick a1b2c3d e4f5g6h # окремі коміти
git cherry-pick main~3..main # діапазон (без main~3)
Корисні параметри:
| Параметр | Що робить |
|---|---|
-x |
додає в повідомлення рядок (cherry picked from commit ...) - видно походження |
--no-commit (-n) |
застосувати зміни без коміту, щоб об'єднати кілька в один |
--continue / --abort |
продовжити після конфлікту чи скасувати |
Конфлікти розв'язуються як при злитті: виправити файли, git add, git cherry-pick --continue.
Чому не варто зловживати:
- дублікати в історії: той самий зміст у двох комітах з різними хешами. Коли гілки згодом зіллються, Git зазвичай розпізнає однакові зміни, але інколи це дає конфлікти;
- заміна злиттю: якщо доводиться переносити десятки комітів, правильніше злити гілку чи переглянути процес роботи з гілками.
Порада: для виправлень у кількох версіях використовуйте -x - тоді з історії релізної гілки видно, звідки прийшла кожна зміна.