14. Отмена коммитов
Иногда вы понимаете, что новые коммиты являются неверными, и хотите их отменить. Есть несколько способов решения этого вопроса, здесь мы будем использовать самый безопасный.
Мы отменим коммит путем создания нового коммита, отменяющего нежелательные изменения.
02 Измените файл и сделайте коммит
Измените файл hello.html на следующий.
Файл: hello.html
html> head> head> body> h1>Hello, World!h1> body> html>
Выполните
git add hello.html git commit -m "Oops, we didn't want this commit"
03 Сделайте коммит с новыми изменениями, отменяющими предыдущие
Чтобы отменить коммит, нам необходимо сделать коммит, который удаляет изменения, сохраненные нежелательным коммитом.
Выполните
git revert HEAD
Перейдите в редактор, где вы можете отредактировать коммит-сообщение по умолчанию или оставить все как есть. Сохраните и закройте файл. Вы увидите:
Результат
$ git revert HEAD [main 86364a1] Revert "Oops, we didn't want this commit" Date: Tue Nov 28 05:51:38 2023 -0600 1 file changed, 1 deletion(-)
Так как мы отменили самый последний произведенный коммит, мы смогли использовать метку HEAD в качестве аргумента для отмены коммита. Мы можем отменить любой произвольной коммит в истории, указав его хеш.
04 Проверьте лог
Проверка лога показывает нежелательные и отмененные коммиты в наш репозиторий.
Выполните
git log
Результат
$ git log 86364a1 2023-11-28 | Revert "Oops, we didn't want this commit" (HEAD -> main) [Alexander Shvets] 6a44bec 2023-11-28 | Oops, we didn't want this commit [Alexander Shvets] b7614c1 2023-11-28 | Added HTML header (tag: v1) [Alexander Shvets] 46afaff 2023-11-28 | Added standard HTML page tags (tag: v1-beta) [Alexander Shvets] 78433de 2023-11-28 | Added h1 tag [Alexander Shvets] 5836970 2023-11-28 | Initial commit [Alexander Shvets]
Эта техника будет работать с любым коммитом (хотя, возможно, возникнут конфликты). Она безопасна в использовании даже в публичных ветках удаленных репозиториев.
05 Далее
Далее давайте посмотрим на технику, которая может быть использована для удаления последних коммитов из истории репозитория.
Как отменить коммит в pycharm
First go to the Git tool window and make sure you’re on the log tab.
It shows the list of commits on the current branch. The most recent one is at the top.
Right-click that commit and in the pop-up window, choose Undo Commit.
That commit has some changes. Let’s keep them in the default changelist. Now the commit tool window says I have changes.
And now that commit…never happened. Let’s look a little deeper.
The Problem
You are editing, say a Markdown file. Let’s leave the Commit tool window and Git tool window open, to see the updates as we go.
You change the title, and make a commit. And for the thousandth consecutive time, you spot a typo, just as the onkeyup event fires when clicking commit. Alas, the commit tool window shows that there are no changes. And, in your Git log, there it is, the mistaken commit.
That means it’s too late, right! Nope, if you haven’t pushed to a “protected” branch, you can clean things up locally. And the IDE puts a convenient UI atop the underlying Git command.
The Solution
In the Git tool window, find the most recent commit. It’s the one at the top. When we select it, we get more detail about the commit. We can double-click the file to see a diff of the change.
When you right-click on a commit, you get some actions you can take on it. Some might be grayed-out. For example, if we right-click on an older, pushed commit, we’ll see “Undo Commit” is grayed-out.
Back to the first commit. Right-click on it, and select “Undo Commit”.
Now, the commit had some changes in them, and these changes need to go somewhere. The IDE’s changelists provide such a place. We’ll stick with the Default Changelist .
This results in our Commit tool window showing the changes when we click ok.
A look in the Git log also shows a change.
That commit is…gone! We’re back to where we were before the commit.
What would this have been like from the command line? Let’s take a look at the Git tool’s Console tab, to see behind the scenes. There it is, the command that we would have had to type, from the command line.
- Undo the last commit
- Manage changelists
Undo changes in Git repository
You can always undo the changes you’ve made locally before you commit them:
- In the Commit tool window Alt+0 , select one or more files that you want to revert, and select Rollback from the context menu, or press Control+Alt+Z .
All changes made to the selected files since the last commit will be discarded, and they will disappear from the active changelist.
Unstage files
By default, PyCharm uses the changelists concept where modified files are staged automatically.
- If a file is already under version control, and you do not want to commit it, you can:
- Remove it from the commit: do not select it in the Changes area of the Commit tool window.
- Move it to another changelist.
Also, by default PyCharm suggests adding each newly created file under version control. You can change this behavior in Settings | Version Control | Confirmation using When files are created and When files are deleted settings respectively.
Undo the last commit
PyCharm allows you to undo the last commit in the current branch.
You cannot undo a commit if it was pushed to a protected branch, that is a branch to which force —push is not allowed (configure protected branches in the Version Control | Git page of the IDE settings Control+Alt+S ) Note that if a branch is marked as protected on GitHub, PyCharm will automatically mark it as protected when you check it out.
- Open the Git tool window Alt+9 and switch to the Log tab.
- Select the last commit in the current branch and choose Undo Commit from the context menu.
- In the dialog that opens, select a changelist where the changes you are going to discard will be moved. You can either select an existing changelist from the Name list, or specify the name of a new changelist (the commit message is used by default).
- Select the Set active option if you want to make the changelist with the changes you are about to discard the active changelist.
- Select the Track context option if you want PyCharm to remember your context and reload currently opened files in the editor when this changelist becomes active.
Revert a pushed commit
If you notice an error in a specific commit that has already been pushed, you can revert that commit. This operation results in a new commit that reverses the effect of the commit you want to undo. Thus, the project history is preserved, as the original commit remains intact.
- Locate the commit you want to revert in the Log tab of the Git tool window Alt+9 , right-click it and select Revert Commit from the context menu. This option is also available from the context menu of a commit in the file History view. The Commit Changes dialog will open with an automatically generated commit message. If you apply this action to multiple commits selected in the Log view, a separate commit will be created to revert each of them.
- If the selected commit contains several files, and you only need to revert some of them, deselect the files you do not want to touch.
- Click Commit to commit a changeset that reverts the changes to the selected files in this particular commit.
Revert selected changes
PyCharm lets you undo selected changes from a pushed commit if this commit contains multiple files and you only need to revert some of them.
- In the Log view select the commit containing the changes you want to discard.
- In the Changed Files pane, right-click the file that you want to revert and select Revert Selected Changes from the context menu. This results in a new commit that reverses the changes you want to undo.
Drop a commit
Unlike reverting a commit, which is reflected in the branch history, you can discard a pushed commit in the current branch without leaving any traces of the operation.
Like any operation that rewrites a branch history, dropping a commit requires a —force push and cannot be performed in protected branches (these can be configured in the Version Control | Git page of the IDE settings Control+Alt+S .
- Select a commit you want to discard in the Log view and choose Drop Commit from the context menu.
Reset a branch to a specific commit
If you notice an error in a set of recent commits and want to redo that part, you can roll back your repository to a specific state. This is done by resetting the current branch HEAD to a specified commit (and optionally resetting the index and working tree if you prefer not to reflect the undo in the history).
- Open the Version Control tool window Alt+9 and switch to the Log tab.
- Select the commit that you want to move HEAD onto and select Reset Current Branch to Here from the context menu.
- In the Git Reset dialog that opens, select how you want your working tree and the index to be updated and click Reset :
- Soft : all changes from commits that were made after the selected commit will be staged (that means they will be moved to the Changes view so that you can review them and commit later if necessary).
- Mixed : changes made after the selected commit will be preserved but will not be staged for commit.
- Hard : all changes made after the selected commit will be discarded (both staged and committed).
- Keep : committed changes made after the selected commit will be discarded, but local changes will be kept intact.
Get a previous revision of a file
If you need to revert a single file instead of discarding a whole commit that includes changes to several files, you can return to a particular version of that file:
- Select the necessary file in any view (the Project tool window, the editor, the Changes view, and so on).
- Select Git | Show History from the main VCS menu or from the context menu of the selection. The History tab is added to the Git tool window showing the history for the selected file and allowing you to review and compare its revisions.
- When you’ve identified the revision you want to roll back to, select it in the list and choose Get from the context menu.
15. Удаление коммитов из ветки
revert из предыдущего раздела является мощной командой, которая позволяет отменить любые коммиты в репозиторий. Однако, и оригинальный и «отмененный» коммиты видны в истории ветки (при использовании команды git log ).
Часто мы делаем коммит, и сразу понимаем, что это была ошибка. Было бы неплохо иметь команду «возврата», которая позволила бы нам сделать вид, что неправильного коммита никогда и не было. Команда «возврата» даже предотвратила бы появление нежелательного коммита в истории git log .
01 Команда reset
Мы уже видели команду reset и использовали ее для согласования области подготовки с выбранным коммитом (в предыдущем уроке мы использовали коммит HEAD ).
Если выполнить команду reset с указанием ссылки на коммит (т.е. метки HEAD , имени ветки или тега, хеша коммита), то команда.
- Изменит текущую ветку, чтобы она указывала на указанный коммит.
- Опционально сбросит область подготовки до соответствия с указанным коммитом.
- Опционально сбросит рабочую директорию до соответствия с указанным коммитом.
02 Проверьте нашу историю
Давайте сделаем быструю проверку нашей истории коммитов.
Выполните
git log
Результат
$ git log 86364a1 2023-11-28 | Revert "Oops, we didn't want this commit" (HEAD -> main) [Alexander Shvets] 6a44bec 2023-11-28 | Oops, we didn't want this commit [Alexander Shvets] b7614c1 2023-11-28 | Added HTML header (tag: v1) [Alexander Shvets] 46afaff 2023-11-28 | Added standard HTML page tags (tag: v1-beta) [Alexander Shvets] 78433de 2023-11-28 | Added h1 tag [Alexander Shvets] 5836970 2023-11-28 | Initial commit [Alexander Shvets]
Мы видим, что два последних коммита в этой ветке — «Oops» и «Revert Oops». Давайте удалим их с помощью сброса.
03 Для начала отметьте эту ветку
Но прежде чем удалить коммиты, давайте отметим последний коммит тегом, чтобы потом можно было его найти.
Выполните
git tag oops
Результат
$ git log 86364a1 2023-11-28 | Revert "Oops, we didn't want this commit" (HEAD -> main, tag: oops) [Alexander Shvets] 6a44bec 2023-11-28 | Oops, we didn't want this commit [Alexander Shvets] b7614c1 2023-11-28 | Added HTML header (tag: v1) [Alexander Shvets] 46afaff 2023-11-28 | Added standard HTML page tags (tag: v1-beta) [Alexander Shvets] 78433de 2023-11-28 | Added h1 tag [Alexander Shvets] 5836970 2023-11-28 | Initial commit [Alexander Shvets]
04 Сброс к коммиту, предшествующему oops
Глядя на историю лога (см. выше), мы видим, что коммит с тегом v1 является коммитом, предшествующим ошибочному коммиту. Давайте сбросим ветку до этой точки. Поскольку ветка имеет тег, мы можем использовать имя тега в команде сброса reset (если она не имеет тега, мы можем использовать хеш коммита).
Выполните
git reset --hard v1 git log
Результат
$ git reset --hard v1 HEAD is now at b7614c1 Added HTML header $ git log b7614c1 2023-11-28 | Added HTML header (HEAD -> main, tag: v1) [Alexander Shvets] 46afaff 2023-11-28 | Added standard HTML page tags (tag: v1-beta) [Alexander Shvets] 78433de 2023-11-28 | Added h1 tag [Alexander Shvets] 5836970 2023-11-28 | Initial commit [Alexander Shvets]
Наша ветка main теперь указывает на коммит v1 , а коммитов «Revert Oops» и «Oops» в ветке уже нет. Параметр —hard указывает, что рабочая директория должна быть приведена к тому состоянию, которое соответствует HEAD -коммиту ветки.
05 Ничего никогда не теряется
Что же случается с ошибочными коммитами? Оказывается, что коммиты все еще находятся в репозитории. На самом деле, мы все еще можем на них ссылаться. Помните, в начале этого урока мы создали для отмененного коммита тег oops ? Давайте посмотрим на все коммиты.
Выполните
git log --all
Результат
$ git log --all b7614c1 2023-11-28 | Added HTML header (HEAD -> main, tag: v1) [Alexander Shvets] 86364a1 2023-11-28 | Revert "Oops, we didn't want this commit" (tag: oops) [Alexander Shvets] 46afaff 2023-11-28 | Added standard HTML page tags (tag: v1-beta) [Alexander Shvets] 6a44bec 2023-11-28 | Oops, we didn't want this commit [Alexander Shvets] 78433de 2023-11-28 | Added h1 tag [Alexander Shvets] 5836970 2023-11-28 | Initial commit [Alexander Shvets]
Мы видим, что ошибочные коммиты не исчезли. Они все еще находятся в репозитории. Просто они отсутствуют в ветке main . Если бы мы не отметили их тегами, они по-прежнему находились бы в репозитории, но не было бы никакой возможности ссылаться на них, кроме как при помощи хешей этих коммитов. Коммиты, на которые нет ссылок, остаются в репозитории до тех пор, пока не будет запущен сборщик мусора.
06 Опасность сброса
Сброс в локальных ветках, как правило, безопасен. Последствия любой «аварии» как правило, можно восстановить простым сбросом с помощью нужного коммита.
Однако, если ветка уже стала общедоступной на удаленных репозиториях, сброс может сбить с толку других пользователей ветки.