Git Reset vs Revert vs Rebase

У цій статті ви дізнаєтесь про різні способи використання комітів у Git.

Як розробник, ви неодноразово стикалися з такими ситуаціями, коли ви хотіли б повернутися до одного з ваших попередніх комітів, але не знаєте, як це зробити. І навіть якщо ви знаєте такі команди Git, як reset, revert, rebase, ви не знаєте про відмінності між ними. Отже, давайте почнемо і розберемося, що таке git reset, revert і rebase.

Git Скидання

Git reset — складна команда, яка використовується для скасування змін.

Ви можете розглядати git reset як функцію відкату. За допомогою git reset ви можете переходити між різними комітами. Існує три режими виконання команди git reset: –soft, –mixed і –hard. За замовчуванням команда git reset використовує змішаний режим. У робочому процесі git reset з’являються три внутрішні механізми керування git: HEAD, проміжна область (індекс) і робочий каталог.

Робочий каталог – це місце, де ви зараз працюєте, це місце, де присутні ваші файли. Використовуючи команду git status, ви можете побачити, які файли/папки є в робочому каталозі.

Проміжна область (індекс) — це місце, де git відстежує та зберігає всі зміни у файлах. Збережені зміни відображаються в каталозі .git. Ви використовуєте git add «filename», щоб додати файл до робочої області. І, як і раніше, коли ви запускаєте git status, ви побачите, які файли присутні в робочій області.

Поточна гілка в Git називається HEAD. Він вказує на останній комміт, який стався в поточній гілці перевірки. Він розглядається як покажчик для будь-якого посилання. Після переходу в іншу гілку ГОЛОВА також переходить до нової гілки.

Дозвольте мені пояснити, як git reset працює в жорсткому, м’якому та змішаному режимах. Жорсткий режим використовується для переходу до зазначеного коміту, робочий каталог заповнюється файлами цього коміту, а проміжна область скидається. У програмному скиданні лише вказівник змінюється на вказаний комміт. Файли всіх комітів залишаються в робочому каталозі та проміжній області до скидання. У змішаному режимі (за замовчуванням) вказівник і проміжна область скидаються.

Git Reset Hard

Метою git hard reset є переміщення HEAD до вказаного коміту. Це видалить усі коміти, які відбулися після вказаного коміту. Ця команда змінить історію комітів і вкаже на вказаний коміт.

У цьому прикладі я додам три нові файли, зафіксую їх, а потім виконаю апаратне скидання.

Як ви можете бачити з наведеної нижче команди, прямо зараз нема чого фіксувати.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Тепер я створю 3 файли та додам до них певний вміст.

$ vi file1.txt
$ vi file2.txt
$ vi file3.txt

Додайте ці файли до існуючого сховища.

$ git add file*

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

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

Changes to be committed:

(use "git restore --staged <file>..." to unstage)

new file:
file1.txt

new file:
file2.txt

new file:
file3.txt

Дозвольте мені показати вам перед тим, як фіксувати, що зараз я маю журнал із 3 комітів у Git.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Тепер я закріплюсь у сховищі.

$ git commit -m 'added 3 files'
[master d69950b] added 3 files
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

Якщо я зроблю ls-файли, ви побачите, що нові файли додано.

$ git ls-files
demo
dummyfile
newfile
file1.txt
file2.txt
file3.txt

Коли я запускаю команду журналу в git, у мене є 4 коміти, а HEAD вказує на останній коміт.

$ git log --oneline
d69950b (HEAD -> master) added 3 files
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Якщо я видалю файл file1.txt вручну та виконую статус git, з’явиться повідомлення про те, що зміни не підготовлені для фіксації.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

Changes not staged for commit:

(use "git add/rm <file>..." to update what will be committed)

(use "git restore <file>..." to discard changes in working directory)

deleted:
file1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Тепер я запусту команду жорсткого скидання.

$ git reset --hard
HEAD is now at d69950b added 3 files

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

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Якщо я перевірю журнал git, це виглядатиме так.

$ git log
commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 19:53:31 2020 +0530

added 3 files

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Метою жорсткого скидання є вказівка ​​на вказаний комміт і оновлення робочого каталогу та проміжної області. Дозвольте мені показати вам ще один приклад. Наразі візуалізація моїх комітів виглядає так:

  10 найкращих платформ краудфандингу для вашого бізнесу

Тут я виконаю команду з HEAD^, що означає, що я хочу повернутися до попереднього коміту (один коміт назад).

$ git reset --hard HEAD^
HEAD is now at 0db602e one more commit

Ви бачите, що покажчик голови тепер змінено на 0db602e з d69950b.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Якщо ви перевірите журнал, фіксація d69950b зникла, а заголовок тепер вказує на 0db602e SHA.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

Test

Якщо ви запустите ls-файли, ви побачите, що file1.txt, file2.txt і files3.txt більше не знаходяться в репозиторії, оскільки цей комміт і його файл було видалено після повного скидання.

$ git ls-files
demo
dummyfile
newfile

Git Soft Reset

Так само зараз я покажу вам приклад м’якого скидання. Подумайте, я знову додав 3 файли, як згадувалося вище, і зафіксував їх. Журнал git з’явиться, як показано нижче. Ви бачите, що «soft reset» — це мій останній комміт, і HEAD також вказує на це.

$ git log --oneline
aa40085 (HEAD -> master) soft reset
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Деталі коміту в журналі можна побачити за допомогою наведеної нижче команди.

$ git log
commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 21:01:36 2020 +0530

soft reset

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Тепер, використовуючи програмне скидання, я хочу перейти до одного зі старіших комітів за допомогою SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14

Для цього я виконаю наведену нижче команду. Потрібно передати більше 6 початкових символів SHA, повний SHA не потрібен.

$ git reset --soft 0db602e085a4

Тепер, коли я запускаю журнал git, я бачу, що HEAD скинуто до вказаного мною коміту.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Але різниця тут полягає в тому, що файли коміту (aa400858aab3927e79116941c715749780a59fc9), куди я додав 3 файли, все ще знаходяться в моєму робочому каталозі. Вони не були видалені. Ось чому вам слід використовувати програмне скидання, а не жорстке скидання. У програмному режимі немає ризику втрати файлів.

$ git ls-files
demo
dummyfile
file1.txt
file2.txt
file3.txt
newfile

Git Повернути

У Git команда revert використовується для виконання операції повернення, тобто для повернення деяких змін. Це схоже на команду reset, але єдина відмінність тут полягає в тому, що ви виконуєте новий комміт, щоб повернутися до певного коміту. Коротше кажучи, можна сказати, що команда git revert є комітом.

  Що таке ботнет Mirai і як я можу захистити свої пристрої?

Команда Git revert не видаляє жодних даних під час виконання операції повернення.

Припустімо, я додаю 3 файли та виконую операцію git commit для прикладу повернення.

$ git commit -m 'add 3 files again'
[master 812335d] add 3 files again
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

Журнал покаже новий комміт.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Тепер я хотів би повернутися до одного з моїх минулих комітів, скажімо – «59c86c9 новий коміт». Я б запустив команду нижче.

$ git revert 59c86c9

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

Revert "new commit"

This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is ahead of 'origin/master' by 4 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# modified: dummyfile

Після того, як ви збережете та закриєте файл, ви отримаєте ось результат.

$ git revert 59c86c9
[master af72b7a] Revert "new commit"
1 file changed, 1 insertion(+), 1 deletion(-)

Тепер, щоб внести необхідні зміни, на відміну від скидання, Revert виконав ще одне нове фіксування. Якщо ви знову перевірите журнал, ви знайдете новий комміт через операцію повернення.

$ git log --oneline
af72b7a (HEAD -> master) Revert "new commit"
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Журнал Git буде містити всю історію комітів. Якщо ви хочете видалити коміти з історії, тоді revert не є гарним вибором, але якщо ви хочете зберегти зміни комітів в історії, тоді revert є відповідною командою замість reset.

Git Rebase

У Git rebase — це спосіб переміщення або комбінування комітів однієї гілки над іншою. Як розробник, я б не створював свої функції на гілці master у реальному сценарії. Я працював би над своєю власною гілкою («гілкою функцій»), і коли у мене буде кілька комітів у моїй гілці функцій із доданою функцією, я хотів би перемістити їх до головної гілки.

Перебазування іноді може бути трохи заплутаним для розуміння, оскільки воно дуже схоже на злиття. Мета об’єднання та перебазування обох — взяти коміти з моєї гілки функцій і помістити їх у гілку master або будь-яку іншу гілку. Розглянемо, у мене є графік, який виглядає так:

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

  Які ранги Valorant у порядку?

Отже, тут допоможе перебазування. Цього разу замість виконання git merge я зроблю rebase, де я хочу взяти мої дві коміти гілок функцій і перемістити їх до головної гілки. Rebase візьме всі мої коміти з гілки функцій і перемістить їх поверх комітів головної гілки. Отже, за лаштунками git дублює коміти гілок функцій на гілці master.

Цей підхід дасть вам чіткий прямолінійний графік з усіма комітами підряд.

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

Дозвольте мені показати вам це практично.

Ось як зараз виглядає моя головна гілка. Він має 4 коміти.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Я запусту наведену нижче команду, щоб створити та перейти до нової гілки під назвою feature, і ця гілка буде створена з 2-го коміту, тобто 59c86c9

(master)
$ git checkout -b feature 59c86c9
Switched to a new branch 'feature'

Якщо ви перевірите журнал у гілці функцій, він містить лише 2 коміти, що надходять від головного (основного).

(feature)
$ git log --oneline
59c86c9 (HEAD -> feature) new commit
e2f44fc (origin/master, origin/HEAD) test

Я створю функцію 1 і передам її до гілки функцій.

(feature)
$ vi feature1.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 1'
[feature c639e1b] feature 1
1 file changed, 1 insertion(+)
create mode 100644 feature1.txt

Я створю ще одну функцію, тобто функцію 2, у гілці функцій і зафіксую її.

(feature)
$ vi feature2.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 2'
[feature 0f4db49] feature 2
1 file changed, 1 insertion(+)
create mode 100644 feature2.txt

Тепер, якщо ви перевірите журнал гілки функцій, він містить два нових коміти, які я виконав вище.

(feature)
$ git log --oneline
0f4db49 (HEAD -> feature) feature 2
c639e1b feature 1
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Тепер я хочу додати ці дві нові функції до головної гілки. Для цього я використаю команду rebase. З гілки функцій я буду перебазовувати гілки master. Це призведе до повторного прив’язування моєї гілки функцій до останніх змін.

(feature)
$ git rebase master
Successfully rebased and updated refs/heads/feature.

Тепер я збираюся перевірити головну гілку.

(feature)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

І, нарешті, перебазуйте головну гілку на мою функцію. Це візьме ці два нових коміти на моїй гілці функцій і відтворить їх поверх моєї головної гілки.

(master)
$ git rebase feature
Successfully rebased and updated refs/heads/master.

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

(master)
$ git log --oneline
766c996 (HEAD -> master, feature) feature 2
c036a11 feature 1
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Це було все про команди reset, revert і rebase у Git.

Висновок

Це було все про команди reset, revert і rebase у Git. Сподіваюся, цей покроковий посібник був корисним. Тепер ви знаєте, як грати зі своїми комітами відповідно до потреби за допомогою команд, згаданих у статті.