• База знань
  • /
  • Блог
  • /
  • Wiki
  • /
  • ONLINE CHAT
+380 (44) 364 05 71

Стаття також доступна російською (перейти до перегляду).

Пояснення, як скасувати зміни в Git за допомогою reset, revert і restore у різних сценаріях без втрати даних

Зміст:

При роботі над проектом з використанням системи контролю версій Git нерідко трапляються ситуації, коли потрібно скасувати внесені у код зміни або, як кажуть «скасувати комміт». Але простого рішення тут немає, оскільки не існує універсальної кнопки «відкатити все» для всіх випадків. Все залежить від конкретної ситуації, котра б, зокрема,  враховувала – як давно були внесені зміни, чи були вони проіндексовані чи «запушені» тощо. Помилка у виборі команди для відміни операції може призвести до неочікуваних наслідків, або до переписування історії. Щоб упевнено обрати правильний інструмент, необхідно розуміти призначення кожного з них та застосовувати відповідно до ситуації. Про це і піде мова у статті з використанням конкретних прикладів.  

Що означає «скасувати комміт» в Git

Робота Git побудована на основі постійного контролю та фіксації станів файлів проекту, наприклад, під час роботи пайплайну CI/CD, спираючись на життєвий цикл станів (див. Малюнок 1). 

Життєвий цикл станних файлів

Малюнок 1. Життєвий цикл станів файлів.

Відповідно до цього, всі файли проекту можуть бути, або під версійним контролем (відслідковувані), або ні (стан Untracked). 

До відслідковуваних відносяться ті об’єкти файлової системи, котрі були присутні при фіксації останнього стану проекту. Вони можуть бути:

  • Немодифікованими (Unmodified);

  • Модифікованими (Modified);      

  • Підготовленими до індексації (Staged).  

До Untracked-файлів відносяться будь-які файли робочого каталогу, котрі не були присутні при фіксації останнього стану проекту.     

Цикл починається зі створення / клонування Git репозиторію. На цей момент всі файли будуть відслідковуваними та знаходитися у стані Unmodified. 

Після редагування (Edit the file) файл (и) переходить (ять) у стан Modified.

Після передачі змін до індексу (Stage the file) об’єкти переходять у стан Staged та вважаються підготовленими до індексації для передачі змін до репозиторію.

Після фіксації змін у локальному репозиторії (Commit) відбувається перехід об’єктів до стану Unmodified, після чого цикл повторюється.  

Таким чином, ми прийшли до розуміння поняття комміту, котрий представляє собою дію користувача по передачі підготовлених змін до репозиторію (команда Commit), створюючи точку фіксації поточного стану проекту. Відповідно, відміна комміту означає відкат змін на попередній або інший наперед визначений стан. Однак, нерідко трапляються ситуації, коли зміни ще не дійшли до фіксації у локальному репозиторії або, навпаки, були вже відправлені до віддаленого репозиторію, тобто, «запушені». Для кожного такого випадку застосовуються відповідні команди зі своїм набором опцій.  

Системою Git передбачена наявність команди для відслідковування станів всіх файлів проекту. Її назва – git status. 

У випадку відсутності відредагованих та / або невідслідковуваних файлів проекту, вказана команда видасть наступний результат:   

On branch master
nothing to commit, working tree clean

Команда повідомляє, що у гілці master немає змін для фіксації і робочий каталог чистий. Таким чином, Git включається в роботу лише в тому випадку, коли з’являються відмінності між робочою версією проекту і репозиторієм.    

Тут ми прийшли до поняття гілки. Гілка – це напрямок розвитку проекту, котрому може бути присвоєне довільне ім’я для зручного керування. Стандартне ім’я – master або main. Для головної гілки віддаленого репозиторію часто використовується ім’я origin.   

Ще одне важливе поняття – HEAD. Це вказівник на поточний комміт. Стандартно він вказує на останній комміт поточної гілки. Але може бути перенаправлений, наприклад, за допомогою команди git reset, що буде розглянуто нижче.  

Швидка шпаргалка: яку команду і коли застосовувати

У Таблиці 1 наведені розповсюджені варіанти команд для здійснення «відкатів» різних рівнів та показаний їх вплив на область індексації (Staged) та робочий каталог проекту (Work dir). Стовпчик Безпека показує рівень безпеки виконання відповідної команди.   

Таблиця 1. Розповсюджені команди для «відкату» змін та їх вплив на Git середовище.

КомандаПризначенняStagedWork dirБезпека
reset <commit> Перемикання HEAD на вказаний комміт без збереження змін в індексі + - +
reset --soft <commit> Перемикання HEAD на вказаний комміт зі збереженням змін в індексі - - +
reset --hard <commit> Скасування всіх змін вказаного комміта в індексі та робочому каталозі + + -
restore --staged <file> Скасування індексації файлу + - +
restore <file> Відкат змін у файлі + + -
commit --amend Виправлення останнього комміта + - +
revert <commit> Безпечне скасування вже відправленого комміта новим - - +
reflog Відновлення після невдалих дій - - +

Як виправити останній комміт – git commit --amend

Для демонстрації можливостей цієї та інших команд нам знадобиться Git-репозиторій та новий веб-проект розгорнутий на VPS-хостінгу. Створимо їх.  

Створення репозиторію та його ініціалізація

Створимо каталог із ім’ям testgit_project та перейдемо до нього. Він буде нашим робочим каталогом проекту (Work dir):

Як виправити останній комміт – git commit --amend

Для демонстрації можливостей цієї та інших команд нам знадобиться Git-репозиторій та новий веб-проект розгорнутий на VPS-хостінгу. Створимо їх.  

Створення репозиторію та його ініціалізація

Створимо каталог із ім’ям testgit_project та перейдемо до нього. Він буде нашим робочим каталогом проекту (Work dir):

$ mkdir testgit_project
$ cd testgit_project

Ініціалізуємо у testgit_project порожній Git репозиторій:

$ git init
Initialized empty Git repository in /home/testing_git/testgit_project/.git/

Тут testing_git – логін користувача із правами sudo; .git – репозиторій.

Зареєструємося в Git:

$ git config --global user.email "alexander@meta.ua"
$ git config --global user.name "Alexander"

Створення веб-проекту та початок роботи з ним

Веб-проект буде складатися із двох html-файлів: test1.html та test2.html.

Створимо файл test1.html:

$ nano test1.html
<html>
  <head>
  </head>
  <body>
    <h1>Testing Git</h1>
     <p> Learning the basics of Git version control starts with examples.</p>
  </body>
</html>

Створимо файл test1.html

Створимо файл test2.html:

$ nano test2.html
<html>
  <head>
  </head>
  <body>
    <h1>Examples</h1>
     <p>Here are some practical examples of using Git.  
     </p>
  </body>
</html>

$ tree

Переглянемо дерево робочого каталогу:

 Дерево рабочего каталога

Додамо файли до індексу:

$ git add *

Переглянемо вміст індексу за допомогою команди:

$ git ls-files -s
100644 3c474c3b0c61a0e527c36376844d358c054e6001 0       test1.html
100644 fe39821a787582e6a91030f8554a7f9bf7bd93b2 0       test2.html

Можна переконатися, що файли вже там.

Створимо комміт:

$ git commit -m 'Adding test1.html and test2.html files'
[master (root-commit) 81efc13] Adding test1.html and test2.html files
 2 files changed, 18 insertions(+)
 create mode 100644 test1.html
 create mode 100644 test2.html

Переглянемо історію:

$ git log
 
commit 81efc131946c225904650ea92923a8ae40289186 (HEAD -> master)
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 18:03:29 2026 +0200
    Adding test1.html and test2.html files

Тут вказівник HEAD вказує на гілку master, котра була створена автоматично.

HEAD завжди хешується, і тому іноді корисно його переглядати. Це можна зробити за допомогою команди:

$ git cat-file -p HEAD
 
tree 5d52e869cf737ccbf38965f1ee81a361f73cf358
author Alexander <alexander@meta.ua> 1774022609 +0200
committer Alexander <alexander@meta.ua> 1774022609 +0200
 
Adding test1.html and test2.html files

Перевіримо стани або статус всіх наявних файлів проекту:

$ git status

On branch master nothing to commit, working tree clean

On branch master
nothing to commit, working tree clean

Ми бачимо, що наразі різниця між робочим каталогом та репозиторієм відсутня. 

Внесемо в файл test1.html зміни, додавши новий абзац з текстом:

$ nano test1.html

Новий абзац: <p> First of all, let's discuss the capabilities of the system.</p>

$ git add test1.html

 Додамо файл до індексу зі створенням відповідного комміта:

$ git add test1.html
$ git commit -m 'Added new <p> tag to test1.html file'
[master c16c606] Added new <p> tag to test1.html file
 1 file changed, 2 insertions(+), 2 deletions(-)

Перевіримо статус:

$ git status

On branch master

On branch master
nothing to commit, working tree clean

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

Внесемо ще одну зміну, додавши тег <h2>:

$ nano test1.html

Новий тег:    <h2>General overview of the system</h2>

$ git status

Перевіримо статус:

$ git status

On branch master

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test1.html
no changes added to commit (use "git add" and/or "git commit -a")

Команда інформує, що індекс порожній і що test1.html має статус modified. Окрім того, команда надає підказки: як можна додати файл до індексної області (git add); як скасувати зміни в робочому каталозі (git restore).

Додамо файл до індексу та знову перевіримо його статус:

$ git add test1.html
$ git status

On branch master

On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test1.html

Тепер команда інформує про те, що зміни підготовлені до відправки до репозиторію, а також надає команду для повернення файлу з індексної зони (git restore --staged).

Створимо комміт:

$ git commit -m 'Added <h2> tag to test1.html file'

[master 27e9cd9] Added <h2> tag to test1.html file

 [master 27e9cd9] Added <h2> tag to test1.html file
 1 file changed, 1 insertion(+)

Знову перевіримо статус:

$ git status

On branch master

On branch master
nothing to commit, working tree clean

Ми бачимо, що знову настала рівновага між робочим каталогом та репозиторієм. І тому Git наразі бездіяльна.  

Використання команди git commit --amend

Нерідко виникає потреба у виправленні повідомлення останнього комміта. Тут допоможе наступна команда:

$ git commit --amend

При її виконанні завантажується редактор із старим повідомленням, котре можна легко виправити:

$ git commit --amend

Цю ж саму команду можна застосовувати у випадку, коли комміт вже створений, але необхідно внести у файл зміни або додати новий. У такому разі послідовність дій буде наступною:

1. Вносимо зміни у файл робочого каталогу:

$ nano test1.html

Додамо текст: «Git version control»)

Git version control

2. Відправляємо файл до індексної області

$ git add test1.htm

3. Створюємо відповідний комміт:

$ git commit --amend --no-edit

Тут ми використали опцію --no-edit для того, щоб залишити повідомлення комміту незмінним. 

--no-edit

[master 616622c] Added one <h2> tag to test1.html file
Date: Fri Mar 20 19:39:10 2026 +0200
1 file changed, 1 insertion(+)

Таким самим чином ми можемо додати і новий файл, а не лише редагований.

Перевіримо історію:

$ git lo

$ git log

commit 616622cb955d8a34b4a8f7c6b2cba610c1e90550 (HEAD -> master)
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 19:39:10 2026 +020
    Added one <h2> tag to test1.html file
 
commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 19:06:48 2026 +0200
 
    Added new <p> tag to test1.html file
 
commit 81efc131946c225904650ea92923a8ae40289186
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 18:03:29 2026 +0200
 
    Adding test1.html and test2.html files

Можна переконатися, що наші дії на історію коммітів не вплинули, оскільки розглянута команда не переміщує HEAD та не змінює гілки. Єдина її «недосконалість» – вона переписує лише останній комміт. Іноді її застосування може вимагати використання force push, однак для загальних гілок це треба робити обережно. 

Як прибрати останній локальний комміт – git reset

Для відміни локальних коммітів дуже часто використовується команда git reset із різними параметрами. Вона впливає на історію, робочий каталог та індексну зону в залежності від застосованих параметрів. Продемонструємо її використання на конкретних прикладах. 

Раніше ми вивели нашу історію коммітів, де останній з них був пов’язаний із додаванням тегу <h2>. Його хеш: 616622cb955d8a34b4a8f7c6b2cba610c1e90550. Наше завдання полягає у видаленні тегу із індексу, залишивши його при цьому у файлі робочого каталогу. У такому разі у нашій гілці розробки HEAD необхідно перемістити на один крок назад.

Команда може виглядати так:

$ git reset --mixed HEAD~1

Або так

$ git reset HEAD~1

Параметр --mixed застосовується у стандартному режимі і тому його можна спеціально не вказувати.

Вихід команди:

Unstaged changes after reset:
M       test1.htm

Перевіримо історію:

$ git log

$ git log

commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 (HEAD -> master)
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 19:06:48 2026 +0200
 
    Added new <p> tag to test1.html file
 
commit 81efc131946c225904650ea92923a8ae40289186
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 18:03:29 2026 +0200
 
    Adding test1.html and test2.html files

Можна переконатися, що комміт із хешем 616622cb955d8a34b4a8f7c6b2cba610c1e90550 тепер недоступний. Вказівник HEAD тепер знаходиться на попередньому комміті.

Перевіримо, чи збереглися зміни в робочому каталозі:

$ nano test1.html

Зміни збереглися (тег <h2> залишився на своєму місці).

Тепер повернемо HEAD назад, вказавши у команді хеш видаленого комміту:

$ git reset 616622cb955d8a34b4a8f7c6b2cba610c1e90550

Перевіримо історію:

$ git log

$ git log

Все гаразд, комміт повернуто. 

Тепер нашим завданням буде лише переміщення HEAD на один крок назад, зберігши при цьому зміни останнього комміта в індексі.  

Для цього існує наступний варіант команди:

$ git reset --soft HEAD~1

Перевіримо історію:

$ git log

$ git log

Перевіримо статус:

$ git status

$ git status

On branch master
Changes to be committed:

  (use "git restore --staged <file>..." to unstage)         modified:   test1.html

Знову повернемо стан індексу назад:

$ git reset 616622cb955d8a34b4a8f7c6b2cba610c1e90550

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

Для цього скористуємося командою:

$ git reset --hard HEAD~1

$ git reset --hard HEAD~1

HEAD is now at c16c606 Added new <p> tag to test1.html fil

Перевіримо історію:

$ git log

$ git log

Перевіримо робочий каталог:

$ nano test1.html

$ nano test1.html

Можна переконатися, що тег <h2> видалений з файлу.

За результатами використання команди можна зробити висновок, що команда з параметром  --hard є небезпечною, оскільки здатна змінювати файли робочого каталогу, а також впливає на історію, що вимагає обережності при її застосуванні.

Як прибрати файл із staged – git restore --staged

Починаючи з версії Git 2.23.0, з’явилася команда git restore як альтернатива git reset для більшості операцій відміни внесених змін. Розглянемо її використання для нашого проекту. 

Спочатку розглянемо випадок, коли необхідно повернути лише один файл із індексної зони.

Створимо новий файл та відправимо його в індекс:

$ touch test3.html
$ git add test3.html

Перевіримо статус:

$ git status

On branch master

On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   test3.htm

Команда надає підказку, як можна повернути файл із індексної зони, і тому скористуємося нею:

$ git restore --staged test3.htm

Перевіримо статус

$ git status

On branch master

 On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        test3.html
nothing added to commit but untracked files present (use "git add" to track)

Можна переконатися, що файл вже не в індексі. Слід зауважити, що команда не впливає на стан файлів робочого каталогу і тому файл залишився на місці.

Як повернути лише один файл – git restore

Команду git restore можна застосовувати і в інших випадках, наприклад, для відкату файлу до стану останнього або будь-якого іншого комміта. Розглянемо ці випадки.

Внесемо зміни у файл test1.html, додавши до нього ще один тег <p>:

$ nano test1.html

Новий тег: <p> Now we can move on to the first example.</p>

$ git status

Збережемо внесені зміни та закриємо файл.

Перевіримо статус:

$ git status

$ git status

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test1.html
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        test3.html

Команда надає підказку, як можна повернути файл до попереднього стану. Скористуємося нею:

$ git restore test1.html

Перевіримо файл у робочому каталозі:

$ nano test1.html

$ nano test1.html

Можна переконатися, що останні зміни були видалені з файлу.

Команду також можна використати для повернення файлу до визначеного стану:

$ git restore --source=HEAD~1 test1.html

Перевірка:

$ nano test1.html

Testing git

 Можна переконатися, що файл повернувся до стану попереднього комміту за рахунок переміщення HEAD на одну позицію назад. Звісно, ми могли б перемістити HEAD й на інші позиції – на два, три або більше коммітів назад, але це в даному випадку несуттєво.

Як скасувати комміт, котрий вже відправлений - git revert

В Git існує команда, котра дозволяє анулювати зміни, котрі вже були «запушовані», тобто, відправлені до головної гілки віддаленого репозиторію. Продемонструємо її в роботі.  

Додамо до індексу оновлений нами файл test1.html та сформуємо відповідний комміт:

$git add test1.html
$ git commit -m 'Latest changes in file test1.html'

[master 58a0a79] Latest changes in file test1.html

[master 58a0a79] Latest changes in file test1.html
 1 file changed, 2 insertions(+), 2 deletions(-)

Відправимо всі наші локальні зміни до віддаленого репозиторію за допомогою наступної команди: 

$ git push origin main

Тут origin – ім’я віддаленого репозиторію; main – ім’я гілки.

Переглянемо історію для визначення хеша комміта, котрий потрібно анулювати:

$ git log

commit 58a0a79834d1ae04d7fb463063faa561365e4360 (HEAD -> master)

commit 58a0a79834d1ae04d7fb463063faa561365e4360 (HEAD -> master)
Author: Alexander <alexander@meta.ua>
Date:   Sat Mar 21 18:15:06 2026 +0200
    Latest changes in file test1.html
commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 19:06:48 2026 +0200
 
    Added new <p> tag to test1.html file
 
commit 81efc131946c225904650ea92923a8ae40289186
Author: Alexander <alexander@meta.ua>
Date:   Fri Mar 20 18:03:29 2026 +0200
 
    Adding test1.html and test2.html files

Анулювати проблемний комміт можна за допомогою команди:

$ git revert c16c606bf3ad3ea01f5bd06f2140e21f6b41f821

Тут c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 – хеш комміту, котрий анулюється.

При цьому автоматично буде створений новий комміт (реверт-комміт), котрий відміняє дії вказаного у команді комміту. При цьому реверт-комміт залишиться в історії. 

Можна зробити висновки, що команда не переписує історію, а лише анулює визначений комміт. Це найкращий спосіб для загальної гілки та вже відправлених змін.

Як відновитися після помилки – git reflog

Іноді можуть виникати конфліктні ситуації або помилки, котрі потребують відновлення визначених коммітів, наприклад, після невдалого застосування небезпечної команди git reset --hard. У такому разі краще скористатися командою reflog. Продемонструємо це. 

Переглянемо журнал для пошуку хеша комміту:

$ git reflog

$ git reflog

58a0a79 (HEAD -> master) HEAD@{0}: commit: Latest changes in file test1.html
c16c606 HEAD@{1}: reset: moving to HEAD~1
616622c HEAD@{2}: reset: moving to 616622cb955d8a34b4a8f7c6b2cba610c1e90550
c16c606 HEAD@{3}: reset: moving to HEAD~1
616622c HEAD@{4}: reset: moving to 616622cb955d8a34b4a8f7c6b2cba610c1e90550
c16c606 HEAD@{5}: reset: moving to HEAD~1
616622c HEAD@{6}: commit (amend): Added one <h2> tag to test1.html file
9f0dfee HEAD@{7}: commit (amend): Added one <h2> tag to test1.html file
27e9cd9 HEAD@{8}: commit: Added <h2> tag to test1.html file
c16c606 HEAD@{9}: commit: Added new <p> tag to test1.html file
81efc13 HEAD@{10}: commit (initial): Adding test1.html and test2.html files

Наприклад, ми маємо потребу у відновленні комміта з хешем c16c606. Для цього переключимося на нього за допомогою команди checkout:

$ git checkout c16c606

Note: switching to 'c16c606'

 Note: switching to 'c16c606'

HEAD is now at c16c606 Added new <p> tag to test1.html file

У поточному стані HEAD від'єднаний та вказує на c16c606. Зараз ми можемо створити окрему гілку для збереження відновлених змін. Це можна зробити за допомогою команди:

$ git switch -c new_branch

Switched to a new branch 'new_branch'

Switched to a new branch 'new_branch'

В результаті вказівник HEAD буде вказувати на нову гілку. Перевіримо це:

$ git log

commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 (HEAD -> new_branch)

Результат: commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 (HEAD -> new_branch)

Якщо ж ми не хочемо створювати нової гілки, то скасувати операцію можна за допомогою команди:

$ git switch -

По результатам застосування команди можна зробити висновок, що reflog переміщує HEAD та дозволяє знайти втрачений комміт після невдалого reset --hard.  

Типові помилки і рекомендації

Вкажемо на типові помилки та декотрі рекомендації при використанні команд для скасування внесених змін:

  • Не використовувати reset --hard, якщо не впевнений;

  • Не робити push --force до загальної гілки без усвідомлення наслідків;

  • Для вже створених коммітів зазвичай краще обирати revert;

  • Перед небезпечними діями корисно переглянути git status та git log --oneline.

На завершення

  • Для безпечної роботи важливо не запам’ятовувати всі команди поспіль, а розуміти сценарії їх використання;

  • У більшості випадків вибір відбувається між reset, revert і restore;

  • Команда reflog — страхування на випадок помилок.

Якщо ви хочете глибше розібратися в автоматизації процесів у Git, рекомендуємо прочитати статтю "Що таке Git hooks і навіщо вони потрібні".

Підписуйтесь на наш Telegram-канал https://t.me/freehostua, щоб бути в курсі нових корисних матеріалів.

Дивіться наш канал YouTube на https://www.youtube.com/freehostua.

Ми у чомусь помилилися, чи щось пропустили?

Напишіть про це у коментарях, ми з задоволенням відповімо та обговорюємо Ваші зауваження та пропозиції.

Дата: 31.03.2026
Автор: Олександр Ровник
Голосування

Авторам статті важлива Ваша думка. Будемо раді його обговорити з Вами:

comments powered by Disqus
navigate
go
exit
Дякуємо, що обираєте FREEhost.UA