Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Git: Загальний

20 питань · ~15 хв · Версія v3.0

Увійдіть, щоб продовжити

Індекс і коміти, гілки, merge і rebase, скасування змін, reflog, пошук в історії, віддалені репозиторії, конфлікти й робочі процеси.

За спробу
20
У пулі
100
Проходжень
0
Середній бал
-
Пройшли на 70%+
-

Питання для підготовки

100 питань

Обидві команди переносять зміни з однієї гілки в іншу, але по-різному формують історію.

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 rebase

Спосіб залежить від того, чи коміт уже відправлено в спільний репозиторій.

Коміт ще локальний - 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 revert

git rebase -i відкриває список комітів гілки, де кожному можна задати дію. Так історію з «wip», «fix typo», «ще фікс» перетворюють на кілька змістовних комітів.

git rebase -i main
pick a1b2c3d Add invoice model
squash d4e5f6a fix typo
pick 9f8e7d6 Send invoice email
fixup 1a2b3c4 forgot import
reword 5d6e7f8 wip

Основні дії:

  • pick - лишити коміт як є.
  • reword - змінити повідомлення.
  • squash - злити з попереднім, об'єднавши повідомлення.
  • fixup - злити з попереднім, відкинувши повідомлення.
  • edit - зупинитися на коміті, щоб змінити його чи розбити на кілька.
  • drop або видалити рядок - прибрати коміт.
  • Переставити рядки - змінити порядок комітів.

Зручний прийом - fixup-коміти:

git commit --fixup=a1b2c3d     # «виправлення до коміту a1b2c3d»
git rebase -i --autosquash main # сам поставить fixup у потрібне місце

Навіщо: рев'юеру легше читати кілька логічних комітів, ніж двадцять випадкових; git bisect і git blame працюють точніше; коміт можна відкотити цілком.

Обережно: це переписування історії. Лише для своєї гілки, а потім - git push --force-with-lease. Якщо щось пішло не так, git rebase --abort повертає все як було, а після завершення врятує git reflog.

Багато команд обходяться без цього, вливаючи PR через squash merge - тоді вся гілка стає одним комітом у main.

Докладніше в документації: git rebase: interactive mode

git bisect шукає коміт, що вніс помилку, двійковим пошуком. Ви називаєте один «поганий» коміт (де баг є) і один «хороший» (де його ще не було), а Git щоразу переходить на середину проміжку й питає, чи є там баг.

git bisect start
git bisect bad                 # поточний коміт - зламаний
git bisect good v2.3.0         # у цьому релізі все працювало

# Git перемикається на коміт посередині
# перевіряєте й кажете:
git bisect good    # або
git bisect bad

# ...через кілька кроків:
# a1b2c3d is the first bad commit

git bisect reset               # повернутися, звідки почали

Серед 1000 комітів винуватця знайдено за ~10 перевірок.

Автоматично - якщо є команда, що відповідає «добре/погано» кодом виходу:

git bisect run php artisan test --filter=InvoiceTotalTest

Код 0 - коміт хороший, 1-127 (крім 125) - поганий, 125 - пропустити (наприклад, коміт не збирається).

Що допомагає bisect:

  • Невеликі коміти, кожен з яких працює. Якщо коміт «напівготовий» і падає з іншої причини, bisect заплутається - такі коміти пропускають (git bisect skip).
  • Тест, що відтворює баг, - його пишуть першим, а потім ганяють через bisect run.

Знайшовши коміт, видно не лише рядок, а й контекст: повідомлення, задачу, автора - часто це пояснює, чому баг з'явився.

Докладніше в документації: git bisect

Коміти в Git майже ніколи не зникають одразу. reset --hard, rebase чи видалення гілки лише прибирають посилання на коміти, а самі об'єкти лишаються в репозиторії, доки їх не прибере збирач сміття (за замовчуванням недосяжні записи reflog живуть 30 днів).

git reflog - журнал того, куди вказував HEAD (і кожна гілка) після кожної операції:

git reflog
9f8e7d6 HEAD@{0}: reset: moving to HEAD~3
a1b2c3d HEAD@{1}: commit: Send invoice email
d4e5f6a HEAD@{2}: commit: Add invoice model

Знайшли стан до помилки - повертаємося:

git reset --hard HEAD@{1}          # повернути гілку туди
# або обережніше:
git branch rescue a1b2c3d          # створити гілку на втраченому коміті

Після невдалого rebase шукають у reflog запис перед rebase (start). Ще простіше - ORIG_HEAD: rebase, merge і reset зберігають у ньому попереднє положення.

Що reflog не врятує:

  • Незакомічені зміни після reset --hard чи checkout -- file: їх у репозиторії не було. Лише якщо їх додавали в індекс (git add), об'єкти можна знайти через git fsck --lost-found.
  • Чужий репозиторій: reflog локальний. Коміт, втрачений на сервері після force push, шукають у reflog того, хто його мав.
  • Записи, старші за термін зберігання, після git gc.

Висновок для команди: закомітьте - і зміни майже неможливо втратити. Небезпечна лише незакомічена робота.

Докладніше в документації: git reflog

Прочитати - ще не значить знати

20 питань, по одному на екран, ~15 хв. Після завершення - розбір кожної помилки з посиланням на питання.