У цій статті ви дізнаєтесь про різні способи використання комітів у 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
Метою жорсткого скидання є вказівка на вказаний комміт і оновлення робочого каталогу та проміжної області. Дозвольте мені показати вам ще один приклад. Наразі візуалізація моїх комітів виглядає так:
Тут я виконаю команду з 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 є комітом.
Команда 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 або будь-яку іншу гілку. Розглянемо, у мене є графік, який виглядає так:
Припустімо, ви працюєте в команді з іншими розробниками. У такому випадку ви можете уявити, що це може стати дуже складним, якщо у вас є купа інших розробників, які працюють над різними гілками функцій, і вони об’єднують численні зміни. Стає заплутаним відстежувати.
Отже, тут допоможе перебазування. Цього разу замість виконання 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. Сподіваюся, цей покроковий посібник був корисним. Тепер ви знаєте, як грати зі своїми комітами відповідно до потреби за допомогою команд, згаданих у статті.