Теорія
Читаємо історію: повідомлення, diff і blame
Як відкрити історію файлу, прочитати diff рядок за рядком і через blame простежити будь-який рядок до його коміту, pull request та issue.
~9 хв читання · 10 вправ · 250 балів
Перед початком
Хто це змінив і навіщо?
Знову в продуктовій команді Котокорму. Минулого разу зміну ціни доставки вдалося знайти через список файлів, довгим шляхом. Питання на кшталт «хто це змінив і навіщо?» надходитимуть і далі, тож сьогодні ти навчишся відповідати на них як слід.
Ти відкриєш історію одного файлу, прочитаєш diff рядок за рядком і простежиш будь-який рядок сайту до коміту, pull request та issue, з яких він з'явився.
Власна історія файлу
У продуктовій команді Котокорму найчастіше питання — «хто це змінив і навіщо?». Найшвидший шлях до відповіді зазвичай лежить через один файл.
Відкрий файл, наприклад config/prices.json. У його правому верхньому куті є кнопка History. Вона показує всі коміти, які будь-коли змінювали саме цей файл, згруповані за датами, кожен із повідомленням, автором і коротким хешем. У всьому репозиторії можуть бути сотні комітів, а історія файлу коротка й предметна: тут одинадцять.

Пам'ятай про напрям: GitHub показує історію від найновішого, а схеми в цьому курсі — від найстарішого. Розробники бачать той самий список у терміналі через git log -- config/prices.json; -- каже Git, що далі йде шлях до файлу.
Інші двері ведуть деінде. Лічильник 412 Commits відкриває історію всієї гілки, README знайомить із проєктом, а вкладка Actions показує автоматичні перевірки й збірки. Коли питання про один файл — починай із цього файлу.
- ТакВідкрити
config/prices.json→HistoryОдинадцять доречних комітів замість сотень.
- Не такГортати весь список
412 Commitsу пошуках змін ціниПовільно, і легко щось пропустити.
Історія файлу проти історії гілки
Вигляд файлу й вигляд гілки показують ті самі коміти, просто інакше відфільтровані. З останніх шести комітів у main файл config/prices.json могли зачепити лише два; History файлу покаже тільки їх, а історія гілки — всі шість.
5b8f3a2Update cat food photos77c1e90Add express delivery price9d04e61Fix typo in footera41c09eRaise delivery price to 59 UAHe7c3b19Add summer promo bannerf02a6d4Update FAQ answersmain
Граф комітів, від найстарішого: Останні шість комітів у `main` 1. коміт 5b8f3a2 у main "Update cat food photos" 2. коміт 77c1e90 у main "Add express delivery price" (виділено) 3. коміт 9d04e61 у main "Fix typo in footer" 4. коміт a41c09e у main "Raise delivery price to 59 UAH" (виділено) 5. коміт e7c3b19 у main "Add summer promo banner" 6. коміт f02a6d4 у main "Update FAQ answers"
Вибирай за тим, що вже знаєш:
- Знаєш файл («чому змінилася ціна доставки?»): відкрий файл і натисни
History. - Знаєш хто чи коли, але не де («Daniel щось змінив минулого вівторка»): відкрий історію гілки через лічильник комітів і скористайся фільтрами. GitHub дає вибрати автора й проміжок дат, і список скорочується до кількох комітів. Кожен із них називає файли, які зачепив.
У терміналі розробники роблять те саме через git log --author="Daniel" --since="2026-10-06" --until="2026-10-07" або git log -- config/prices.json для одного файлу.
Правильний вигляд, вибраний одразу, заощаджує більше часу, ніж швидке читання. Добра звичка — проговорити питання й помітити, що з трьох у тебе є: файл, людина чи дата.
- Так«Щось змінилося минулого вівторка» → історія гілки, фільтр за автором і датою
- Так«Чому змінилася ця ціна?» →
Historyфайлу - Не такВідкривати файл за файлом у надії помітити зміну
Починай із того, що знаєш, а не з дерева папок.
Як читати diff
Натисни на будь-який коміт, і побачиш його diff: що змінилося порівняно з попередньою версією. Ось diff файлу config/prices.json у коміті a41c09e:
"currency": "UAH",
- "delivery": 49,
+ "delivery": 59,
"freeDeliveryFrom": 800
Три види рядків:
- Рядок, що починається з
-(на GitHub червоний), — це рядок у попередньому вигляді; цей коміт його видалив. - Рядок, що починається з
+(зелений), цей коміт додав. - Сірий рядок без знака — незмінний контекст, щоб було зрозуміло, де ти у файлі.
Над кожним файлом GitHub показує лічильники на кшталт +12 −3: дванадцять рядків додано й три видалено в цьому файлі. Вони кажуть про розмір зміни ще до читання. Маленьке +1 −1 — зазвичай зміна одного значення; +300 −2 — нова сторінка чи фіча.
У терміналі такий самий вигляд дає git show a41c09e, з тими самими плюсами й мінусами.
- Так
- "delivery": 49,, потім+ "delivery": 59,→ «доставка змінилася з 49 на 59» - Не такЧитати сірі рядки контексту як частину зміни
Без знака — там нічого не змінювалося.
Редагування — це мінус і плюс
Diff працює рядок за рядком і знає лише дві операції: рядок видалили або рядок додали. Окремого типу «відредаговано» немає. Тож коли Daniel міняє 49 на 59, diff показує старий рядок червоним із - і новий зеленим із +, один під одним.
Щоб редагування було легко помітити, GitHub ще й підсвічує конкретні символи, які відрізняються в цій парі, тож 4 і 5 виділяються темнішим відтінком. Це сигнал, що перед тобою один відредагований рядок, а не два не пов'язані.
Для довших змін є два режими перегляду. Типовий unified ставить старі й нові рядки в одну колонку. Режим Split показує стару версію ліворуч, а нову праворуч — багатьом так легше читати відредаговані абзаци тексту.
Коли розповідаєш комусь про зміну, перекладай пару назад людською мовою: «доставка подорожчала з 49 до 59 грн», а не «один рядок видалено, один додано».
- Так«Доставка подорожчала з 49 до 59 грн».
Пару мінус/плюс перекладено людською мовою.
- Не так«Хтось видалив ціну доставки, а потім додав нову».
Це одне редагування, а не видалення плюс окреме додавання.
Як читати більший diff
Ціни — це зміни на один рядок; зміни текстів багатші. Надійний порядок для будь-якого diff:
- Перевір назву файлу над diff. Те саме речення може жити у двох місцях, а diff завжди належить одному файлу.
- Прочитай рядки з мінусом: що було раніше.
- Прочитай рядки з плюсом: що є тепер. Мінус одразу перед схожим плюсом — це редагування; плюс без пари — новий текст.
- Порахуй і порівняй із лічильниками над файлом.
Візьмімо diff файлу pages/faq/delivery.md:
## Delivery
-Orders arrive in 2–3 days.
+Orders arrive in 1–2 days in Kyiv and 2–4 days elsewhere.
Free delivery from 800 UAH.
+Express delivery: 99 UAH.
Речення про терміни доставки переписали (один мінус, один плюс), а рядок про експрес-доставку новий (плюс без пари). Заголовок і рядок про безкоштовну доставку без знака, тож вони не змінювалися. Додано два рядки, видалено один: +2 −1.
- ТакСпершу мінуси, потім плюси, тоді рахуй; звір результат із
+2 −1 - Не такЗгадувати незмінний рядок «Free delivery from 800 UAH» як частину зміни
Немає знака — немає зміни.
Blame: звідки взявся цей рядок?
History працює, коли знаєш файл. А коли дивишся на один конкретний рядок і хочеш знати його історію, перемкни вигляд файлу з Code на Blame.
Біля кожного рядка GitHub покаже останній коміт, що його змінив: автора, повідомлення коміту й дату. Кольорова смужка збоку йде від старіших змін до новіших, тож свіжі правки видно одразу. Рядки, змінені одним комітом, згруповано разом.

Назва звучить різко, але ніхто не використовує blame, щоб шукати винних. Розробники постійно запускають його на власному коді, здебільшого щоб згадати, що мали на увазі кілька місяців тому. Читай це як «звідки взявся цей рядок?». У терміналі це git blame config/prices.json, а на GitHub — одна кнопка.
Чого blame не показує: хто першим створив файл (лише останню зміну кожного рядка) і хто схвалив зміну на рев'ю (це в pull request). І, як усе в Git, він нічого не знає про клієнтів.
- Так«Звідки взявся
"delivery": 59?» →Blame→a41c09e, Daniel, 3 червня - Не так«Blame показує Daniel, отже, ціна доставки — його ідея».
Blame каже, хто змінив рядок, а не хто вирішив. Навіщо — у pull request та issue.
Від рядка до причини
Blame дає коміт, але коміт каже лише що і коли. Навіщо і хто погодив — за один-два кліки далі. Для ціни доставки Котокорму повний ланцюжок такий:
- Відкрий
config/prices.json. - Перемкни на
Blame: рядок"delivery": 59вказує на комітa41c09e. - Натисни на повідомлення коміту. Воно закінчується на
(#128)— посилання на pull request. - Відкрий pull request #128: обговорення, рев'ю і хто його схвалив. В описі написано «Closes #120».
- Прочитай issue #120, відкрите фінвідділом: «Courier rates up from 1 June».
a41c09e Raise delivery price to 59 UAH
Що змінилося, хто і коли (знайдено через Blame)
Raise delivery price after courier rate change#128
Обговорення: навіщо, хто робив рев'ю, коли змерджили
Courier rates up from 1 June#120
Звідки прийшов запит: фінвідділ
Рядок, коміт, pull request, issue. Чотири кліки — і в тебе вся історія з поясненням фінвідділу, а не тиждень розпитувань.
Ланцюжок працює, лише якщо люди лишають посилання. Тому добрі розробники закінчують повідомлення комітів номером PR і згадують issue в pull request. Коли пишеш issues сам, давай їм зрозумілі назви: з тебе починається чийсь слід.
- ТакРядок →
Blame→ комітa41c09e→ PR #128 → issue #120Від того, що змінилося, до того, навіщо.
- Не такЗупинитися на коміті й угадувати причину
Причина — у PR та issue.
Посилання, що тримають слід
Ланцюжок рядок → коміт → pull request → issue тримається на посиланнях, і GitHub робить їх дешевими.
Будь-який # із номером, як-от #128, стає посиланням на issue чи pull request із цим номером. Issues і pull requests мають у репозиторії спільну нумерацію, тож #120 може бути і тим, і тим; відкрий, щоб побачити.
Деякі слова роблять більше, ніж посилання. Якщо в описі pull request написано Closes #120 (або Fixes #120, Resolves #120), GitHub пов'язує їх, а коли pull request змерджать в основну гілку, зазвичай main, він автоматично закриває issue #120. Закрити не означає видалити: issue лишається, позначене як виконане, з посиланням на pull request, що його завершив.
Для продакта це безкоштовне оновлення статусу. Можна стежити за потрібним issue, а не смикати розробників, а коли воно закриється, пов'язаний pull request покаже, що саме вийшло. Коли пишеш issues, проси команду посилатися на них саме так: це зберігає слід для наступного, хто спитає «навіщо?».
- ТакОпис PR: «Raise delivery price after courier rate change. Closes #120»
Пов'язано, і issue закриється після мерджу.
- Не такОпис PR: «see the finance thing»
Немає посилання — слід обривається тут.
Пастка blame
Blame показує останній коміт, що зачепив рядок, а часом той коміт зовсім не змінив суті. Якщо Daniel перевирівняє відступи в усьому prices.json, blame покаже його на кожному рядку з «Reformat prices.json», хоча жодної ціни він не змінював.
Тож коли blame вказує на коміт із повідомленням «reformat», «rename», «move» чи «lint», не зупиняйся. Є два способи копнути глибше:
- Біля кожного коміту в режимі blame є маленька іконка
View blame prior to this change. Вона показує той самий файл у стані перед цим комітом, із попередніми авторами біля кожного рядка. Повторюй, доки не дійдеш до коміту, який справді змінив значення. - Або відкрий
Historyфайлу й подивись на крок раніше, читаючи diff.

У розробників є й термінальні варіанти, як-от git blame -w, що ігнорує зміни лише в пробілах. Принцип скрізь той самий: уся історія на місці, а останній дотик не завжди справжня зміна.
- ТакBlame показує «Reformat prices.json» →
View blame prior to this change→ коміт, що встановив 1000 - Не так«Blame показує переформатування, отже, поріг 1000 встановив Daniel».
Останній дотик — не завжди справжня зміна.
Готові потренуватися?
Виконайте вправи уроку й отримайте 250 балів, коли пройдете його вперше.
Розпочати вправи