# Читаємо історію: повідомлення, diff і blame

> Як відкрити історію файлу, прочитати diff рядок за рядком і через blame простежити будь-який рядок до його коміту, pull request та issue.

- Курс: [Git і GitHub для продактів](https://productovi.com/uk/courses/git-for-product.md)
- Модуль: Git простими словами
- Урок 3/15, ~27 хв
- Джерело: https://productovi.com/uk/courses/git-for-product/reading-history-diffs-and-blame

## Історія одного файлу

**Alex Tutor:** Минулого разу ми знайшли зміну ціни на сайті Котокорму через список файлів. Сьогодні зробимо це як слід, так, як ти в продуктовій команді розбиратимешся з будь-яким питанням «хто це змінив і навіщо?».

**Alex Tutor:** Відкрий `config/prices.json`. У правому верхньому куті файлу є кнопка `History`. Вона показує всі коміти, які будь-коли змінювали саме цей файл.

**Sofia:** Лише одинадцять комітів, згрупованих за датами. І найновіший угорі, як ти й попереджала.

**Alex Tutor:** Так: GitHub показує історію від найновішого, а наші схеми від найстарішого. Розробники бачать той самий список у терміналі через `git log`.

**Alex Tutor:** У всьому репозиторії сотні комітів, а історія файлу коротка й предметна. Зазвичай це найшвидший шлях до відповіді.

![Перед високою книжковою шафою, заставленою товстими томами, людина витягає одну тоненьку теку.](https://productovi.com/courses/git-for-product/inline/reading-history-diffs-and-blame-illustration-2.webp)

> **Вправа:** Тобі потрібні всі зміни в `config/prices.json` за цей рік. Що відкрити?

## Історія файлу проти історії гілки

**Alex Tutor:** Ось чому перегляд файлу такий зручний. Це останні шість комітів у `main`; лише два виділені зачепили `config/prices.json`.

```text
Граф комітів, від найстарішого: Останні шість комітів у `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 файлу показав би лише два виділені коміти, від найновішого.*

**Sofia:** Тобто історія гілки це все, що робила команда, а історія файлу лише той шматок, який стосується мого питання.

**Alex Tutor:** Саме так: ті самі коміти, просто інакше відфільтровані. В історії гілки є ще фільтри за автором і за датою, коли знаєш, хто чи коли, але не знаєш де.

**Alex Tutor:** А тепер випадок, коли знаєш, хто і приблизно коли, але не знаєш, який файл.

> **Вправа:** Маркетинг каже, що Daniel, веброзробник Котокорму, змінив «щось на сайті» минулого вівторка, але ніхто не знає, у якому файлі. З чого почати?

## Як читати diff

**Alex Tutor:** Натисни на коміт у цьому списку, і побачиш його **diff**: що змінилося порівняно з попередньою версією. Ось diff коміту `a41c09e`.

> **Diff файлу `config/prices.json` у коміті a41c09e**
>
> ```diff
>    "currency": "UAH",
> -  "delivery": 49,
> +  "delivery": 59,
>    "freeDeliveryFrom": 800
> ```
> Рядок із `-` на початку (на GitHub червоний) видалено, рядок із `+` (зелений) додано. Рядки без знака не змінювалися, це контекст.

**Sofia:** Тобто рядок не редагували, а видалили й додали заново?

**Alex Tutor:** Саме так diff показує редагування: стара версія червоним, нова зеленим, а GitHub ще й підсвічує конкретні символи, які відрізняються. Сірі рядки навколо це контекст, щоб було зрозуміло, де ти у файлі.

**Alex Tutor:** Над кожним файлом GitHub також показує лічильники на кшталт `+1 −1`: скільки рядків додано й видалено. А режим `Split` ставить стару версію ліворуч, а нову праворуч, якщо так легше читати.

> **Вправа:** Зістав кожну частину diff з її значенням.

**Alex Tutor:** Питання Sofia про рядок, який «видалили й додали заново», варте ще однієї перевірки.

> **Вправа:** Коли рядок редагують, diff показує його двічі: стару версію як видалений рядок, а нову як доданий.

## Diff побільше

**Alex Tutor:** Ціни це зміни на один рядок. Зміни текстів виглядають багатше. Ось справжній приклад із FAQ про доставку, про який маркетинг просив минулого місяця.

**Sofia:** Показуй. Хочу перевірити, чи зможу прочитати без підказок.

**Alex Tutor:** Уперед. Спершу читай рядки з мінусом, потім із плюсом, а тоді порахуй, що нового.

**Sofia:** Спершу мінуси, потім плюси, тоді рахуємо. Зрозуміло.

**Alex Tutor:** Одна порада: назва файлу над diff важлива. Те саме речення може жити у двох місцях, а diff завжди належить одному файлу.

> **Вправа:** Ось diff файлу `pages/faq/delivery.md`:
>
> ```diff
>  ## 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.
> ```
>
> Які твердження правдиві? Познач усе, що підходить.

## Blame: хто останнім змінював рядок

**Sofia:** Історія працює, коли я знаю файл. А якщо я дивлюся на конкретний рядок і хочу знати його історію?

**Alex Tutor:** Тоді перемкни вигляд файлу з `Code` на `Blame`. Біля кожного рядка GitHub покаже останній коміт, що його змінив: автора, повідомлення й дату, а ще кольорову смужку від старіших змін до новіших.

**Sofia:** «Blame» звучить трохи агресивно як для командного інструмента.

**Daniel:** Це стара назва, і ніхто не використовує його, щоб шукати винних. Я постійно запускаю blame на власному коді, здебільшого щоб зрозуміти, що я мав на увазі кілька місяців тому.

**Alex Tutor:** Сприймай його як «звідки взявся цей рядок?». У терміналі це `git blame`, а на GitHub одна кнопка.

![Детектив із лупою йде слідами лап від одного виділеного рядка на великій сторінці до будиночка, звідки махає розробник.](https://productovi.com/courses/git-for-product/inline/reading-history-diffs-and-blame-illustration-3.webp)

*Питання не в тому, хто винен, а звідки взявся цей рядок.*

> **Вправа:** Що GitHub показує біля кожного рядка в режимі `Blame`?

## Від рядка до причини

**Alex Tutor:** Тепер увесь ланцюжок для нашої ціни. Blame на рядку `"delivery": 59` показує коміт `a41c09e`. Його повідомлення закінчується на `(#128)`, це pull request. А в PR написано «Closes #120», тобто issue, яке відкрив фінвідділ.

```text
Від одного рядка до причини
- [Commit] a41c09e Raise delivery price to 59 UAH: Що змінилося, хто і коли (знайдено через Blame)
- [Merged] Raise delivery price after courier rate change #128: Обговорення: навіщо, хто робив рев'ю, коли змерджили
- [Closed as completed] Courier rates up from 1 June #120: Звідки прийшов запит: фінвідділ
```

**Sofia:** Рядок, коміт, pull request, issue. Чотири кліки, і в мене вся історія разом з поясненням фінвідділу.

**Alex Tutor:** Ось і весь прийом. Коміт каже, що і коли; pull request та issue кажуть, навіщо і хто погодив.

**Daniel:** Саме тому я закінчую повідомлення комітів на `(#128)` і пишу `Closes #120` у pull requests. Без цих посилань слід обривається на коміті, і доводиться знову всіх розпитувати.

> **Вправа:** Розташуй кроки по порядку, щоб з'ясувати, чому рядок у `config/prices.json` саме такий.

**Alex Tutor:** Звичка Daniel писати `Closes #120` робить більше, ніж просто лишає посилання. Подивімося, що саме.

> **Вправа:** Daniel пише `Closes #120` в описі pull request #128. Що станеться, коли #128 змерджать у `main`?

## Пастка blame

**Daniel:** Одне застереження. Blame показує останній коміт, що зачепив рядок, а часом той коміт лише переніс чи переформатував його.

**Daniel:** Скажімо, я перевирівняю відступи в усьому файлі з цінами. Тоді blame покаже мене на кожному рядку з «Reformat prices.json», хоча жодної ціни я не змінював.

![Людина віддирає смужку свіжої фарби з паркану й відкриває старі вирізані позначки під нею.](https://productovi.com/courses/git-for-product/inline/reading-history-diffs-and-blame-illustration-4.webp)

**Sofia:** І як тоді дістатися глибше?

**Alex Tutor:** Біля кожного коміту в режимі blame є маленька іконка `View blame prior to this change`. Вона показує той самий файл у стані безпосередньо перед цим комітом. Або відкрий `History` файлу й подивись на крок раніше.

> **Вправа:** Blame на рядку `"freeDeliveryFrom": 1000` показує коміт «Reformat prices.json», який лише змінив відступи. Як знайти коміт, що справді встановив значення 1000?

## Закриваємо справу

**Alex Tutor:** Час закрити тікет підтримки. Питання було таке: чому доставка тепер коштує 59 грн, а не 49?

**Sofia:** І я можу відповісти фактами, а не здогадками: коміт, pull request та issue.

**Alex Tutor:** Напиши так, щоб співробітник підтримки міг просто скопіювати це клієнту. Коротко, дружньо, без Git-слів.

**Daniel:** І якщо згадуватимеш дату, бери її з PR, а не з пам'яті. У мене вона жахлива.

> **Вправа:** Ось що вдалося знайти в історії: коміт `a41c09e` «Raise delivery price to 59 UAH» (змерджено 3 червня), PR #128 «Raise delivery price after courier rate change», issue #120 «Courier rates up from 1 June», відкрите фінвідділом. Напиши відповідь на 2–3 речення, яку підтримка може надіслати клієнту, що питає, чому доставка тепер коштує 59 грн, а не 49.

**Alex Tutor:** Коротко про твій набір інструментів: `History`, щоб побачити всі зміни файлу, diff, щоб побачити, що саме змінилося, `Blame`, щоб знайти, звідки взявся рядок, а пов'язані pull request та issue, щоб зрозуміти навіщо.

**Alex Tutor:** Якщо розробник колись надішле тобі команду для термінала, як-от `git blame`, наша [шпаргалка з Git](/uk/tools/git-cheat-sheet#who-why) перекладе її на ці самі кнопки.

**Alex Tutor:** Справу закрито, і модуль 1 пройдено: ти вмієш читати репозиторій, коміт та його історію. Наступний модуль про гілки, де живе паралельна робота.

**Sofia:** І де Daniel зламав кнопку оформлення замовлення, підозрюю.

**Daniel:** Це ще треба довести. І полагодили її за десять хвилин, історія охоче це підтвердить.

- Попередній урок: [Репозиторій і коміти: як читати сторінку репозиторію](https://productovi.com/uk/courses/git-for-product/reading-a-repository-page.md)
- Наступний урок: [Гілки в Git: паралельні версії проєкту](https://productovi.com/uk/courses/git-for-product/branches.md)

---
Productovi Learning. Безкоштовно, читати можна без реєстрації.
