В этой статье вы узнаете о различных способах работы с коммитами в Git.
Как разработчик, вы не раз сталкивались с такими ситуациями, когда хотели бы вернуться к одному из своих предыдущих коммитов, но не знаете, как это сделать. Даже если вы знакомы с такими командами Git, как reset, rollback, rebase, вы не знаете о различиях между ними. Итак, давайте начнем и разберемся, что такое git reset, rollback и rebase.
Содержание
Git сброс
Git reset — это сложная команда, которая используется для отмены изменений.
Вы можете думать о git reset как о функции отката. С помощью git reset вы можете переключаться между разными коммитами. Есть три способа запустить команду git reset: –soft, –miked и –hard. По умолчанию команда git reset использует смешанный режим. В рабочем процессе сброса git в игру вступают три внутренних механизма управления git: HEADER, промежуточная область (индекс) и рабочий каталог.
Рабочий каталог — это то место, где вы сейчас работаете, это место, где находятся ваши файлы. Используя команду git status, вы можете увидеть, какие файлы/папки присутствуют в рабочем каталоге.
Промежуточная область (индекс) — это место, где git отслеживает и сохраняет все изменения в файлах. Сохраненные изменения отражаются в каталоге .git. Используйте git add «filename», чтобы добавить файл в промежуточную область. И, как и раньше, при запуске git status вы увидите, какие файлы присутствуют в тестовой области.
Текущая ветка в Git называется HEAD. Указывает последнюю фиксацию, которая произошла в текущей ветке проверки. Он рассматривается как указатель на любую ссылку. Когда вы переходите в другую ветку, ГОЛОВА также перемещается в новую ветку.
Позвольте мне объяснить, как git reset работает в жестком, мягком и смешанном режимах. Жесткий режим используется для переключения на зафиксированную фиксацию, рабочий каталог заполняется файлами этой фиксации, а промежуточная область сбрасывается. При мягком сбросе изменяется только указатель на указанную фиксацию. Все файлы коммитов остаются в рабочем каталоге и промежуточной области до сброса. В смешанном режиме (по умолчанию) курсор и промежуточная область сбрасываются.
Git сбросить жесткий
Цель жесткого сброса git — переместить 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*
Когда вы снова запустите команду состояния, она отобразит новые файлы, которые я только что создал.
$ 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..." 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
Когда я запускаю команду log в 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
Если я пойду и удалю файл1.tkt вручную и сделаю статус 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..." to update what will be committed) (use "git restore ..." 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 Date: Mon May 17 19:53:31 2020 +0530 added 3 files commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek 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 Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek Date: Mon May 17 00:16:33 2020 +0530 Test
Если вы запустите ls-files, вы увидите, что файлов file1.tkt, file2.tkt и files3.tkt больше нет в репозитории, потому что этот коммит и его файл были удалены после полной перезагрузки.
$ git ls-files demo dummyfile newfile
Мягкий сброс Git
Аналогично, сейчас я покажу вам пример софт-ресета. Обратите внимание, я повторно добавил 3 файла, как указано выше, и зафиксировал их. Журнал Git будет выглядеть так, как показано ниже. Вы можете видеть, что «мягкий сброс» — это мой последний коммит, и 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 Date: Mon May 17 21:01:36 2020 +0530 soft reset commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek 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 Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek 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 используется для выполнения операции отката, т.е. чтобы отменить некоторые изменения. Она похожа на команду сброса, но с той лишь разницей, что вы выполняете новую фиксацию, чтобы вернуться к определенной фиксации. Короче говоря, справедливо сказать, что команда 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 new commit». Я бы запустил команду ниже.
$ 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 перебазировать
В Git rebase — это способ перемещения или объединения коммитов из одной ветки в другую. Как разработчик, я бы не стал создавать свои функции в основной ветке в реальном сценарии. Я бы работал над своей веткой («ветка с ограниченным доступом»), и когда у меня будет несколько коммитов в моей ветке с добавленной функцией, я бы хотел переместить ее в основную ветку.
Rebase иногда может быть немного запутанным для понимания, потому что он очень похож на слияние. Цель слияния и перебазирования обоих состоит в том, чтобы взять коммиты из моей функциональной ветки и поместить их в основную ветку или любую другую ветку. Учтите, у меня есть такая диаграмма:
Предположим, вы работаете в команде с другими разработчиками. В этом случае вы можете себе представить, что это может стать очень сложным, когда у вас есть группа других разработчиков, работающих над разными ветками функций, и они объединяют несколько изменений. Это сбивает с толку.
Так что здесь поможет rebase. На этот раз вместо слияния git я собираюсь сделать перебазирование, где я хочу взять две фиксации ветки функций и переместить их в основную ветку. Rebase возьмет все мои коммиты из ветки функций и переместит их в начало коммитов основной ветки. Итак, за кулисами git дублирует фиксацию функциональной ветки в основной ветке.
Этот подход даст вам четкий прямой график со всеми фиксациями в последовательности.
Позволяет легко отслеживать, куда ушли коммиты. Вы можете себе представить, что если вы работаете в команде со многими разработчиками, все коммиты по-прежнему выполняются последовательно. Так что очень легко отслеживать, даже если у вас есть много людей, работающих над одним и тем же проектом в одно и то же время.
Позвольте мне показать вам это практически.
Вот так сейчас выглядит моя основная ветка. Есть 4 коммита.
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Я выполню приведенную ниже команду, чтобы создать и переключиться на новую ветку с именем function, и эта ветка будет создана из 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
Теперь я хочу добавить эти две новые функции в ветку master. Для этого я буду использовать команду rebase. Из функциональной ветки я перебазирую основную ветку. Что это сделает, так это повторно прикрепит мою ветку функций к последним изменениям.
(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
Это было все о сбросе, откате и перемещении команд в Git.
Заключение
Это было все о сбросе, откате и перемещении команд в Git. Я надеюсь, что это пошаговое руководство было полезным. Теперь вы знаете, как играть со своим коммитом по мере необходимости, используя команды, упомянутые в статье.