Git отменить изменения в файле
Можно сказать, что команда git revert представляет собой типичную команду отмены. Однако принцип ее действия отличается от привычной отмены изменений. Вместо удаления коммита из истории проекта эта команда отменяет внесенные в нем изменения и добавляет новый коммит с полученным содержимым. В результате история в Git не теряется, что важно для обеспечения целостной истории версий и надежной совместной работы.
Отмена с помощью команды revert необходима, когда нужно обратить изменения, внесенные в некоем коммите из истории проекта. Это может быть полезно, если баг появился в проекте из-за конкретного коммита. Вам не потребуется вручную переходить к этому коммиту, исправлять его и выполнять коммит нового снимка состояния — команда git revert сделает это автоматически.
Рабочий каталог
Рабочий каталог обычно синхронизируется с локальной файловой системой. Чтобы отменить изменения в рабочем каталоге, можно просто отредактировать файлы с помощью привычного редактора. Git имеет два инструмента для управления рабочим каталогом. Это команда git clean — удобная утилита для отмены изменений в рабочем каталоге, и команда git reset , которую можно вызвать с параметрами --mixed или --hard , чтобы сбросить изменения в рабочем каталоге.
Раздел проиндексированных файлов
Команда git add используется для добавления изменений в раздел проиндексированных файлов. Команда git reset предназначена главным образом для отмены изменений в разделе проиндексированных файлов. Команда reset с параметром --mixed перемещает все ожидающие изменения из раздела проиндексированных файлов обратно в рабочий каталог.
Отмена коммита с помощью git reset
Рассмотрение этой стратегии отмены мы продолжим на нашем рабочем примере. Команда git reset — это расширяемая команда с разнообразными функциями и вариантами использования. Если мы выполним команду git reset --hard a1e8fb5 , история коммитов будет сброшена до указанного коммита. Просмотр истории коммитов с помощью команды git log теперь будет выглядеть так:
Вывод команды log показывает, что коммиты e2f9a78 и 872fa7e больше не существуют в истории. На этом этапе мы можем продолжить работу и создавать новые коммиты так, словно «безумных» коммитов никогда не было. Этот метод отмены изменений оставляет историю максимально чистой. Отмена с помощью команды reset отлично подходит для локальных изменений, но при работе в общем удаленном репозитории создает сложности. Если у нас есть общий удаленный репозиторий, в котором с помощью команды push опубликован коммит 872fa7e , и мы попытаемся выполнить команду git push для ветки, в которой с помощью команды reset была сброшена история, система Git обнаружит это и выдаст ошибку. Git будет считать, что публикуемая ветка не была обновлена, поскольку в ней отсутствуют коммиты. В таких случаях лучше использовать отмену с помощью команды git revert .
Отмена коммита снимка
Технически существует несколько стратегий отмены коммитов. В дальнейших примерах предполагается, что история коммитов выглядит следующим образом:
Займемся отменой коммита 872fa7e Try something crazy . Возможно, безумный эксперимент зашел слишком далеко.
EXAMPLES
You are happily working on something, and find the changes in these files are in good order. You do not want to see them when you run git diff , because you plan to work on other files and changes with these files are distracting.
Somebody asks you to pull, and the changes sound worthy of merging.
However, you already dirtied the index (i.e. your index does not match the HEAD commit). But you know the pull you are going to make does not affect frotz.c or filfre.c , so you revert the index changes for these two files. Your changes in working tree remain there.
Then you can pull and merge, leaving frotz.c and filfre.c changes still in the working tree.
This is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message, or both. Leaves working tree as it was before "reset".
Make corrections to working tree files.
"reset" copies the old head to .git/ORIG_HEAD ; redo the commit by starting with its log message. If you do not need to edit the message further, you can give -C option instead.
See also the --amend option to git-commit[1].
You have made some commits, but realize they were premature to be in the master branch. You want to continue polishing them in a topic branch, so create topic/wip branch off of the current HEAD .
Rewind the master branch to get rid of those three commits.
Switch to topic/wip branch and keep working.
The last three commits ( HEAD , HEAD^ , and HEAD~2 ) were bad and you do not want to ever see them again. Do not do this if you have already given these commits to somebody else. (See the "RECOVERING FROM UPSTREAM REBASE" section in git-rebase[1] for the implications of doing so.)
Try to update from the upstream resulted in a lot of conflicts; you were not ready to spend a lot of time merging right now, so you decide to do that later.
"pull" has not made merge commit, so git reset --hard which is a synonym for git reset --hard HEAD clears the mess from the index file and the working tree.
Merge a topic branch into the current branch, which resulted in a fast-forward.
But you decided that the topic branch is not ready for public consumption yet. "pull" or "merge" always leaves the original tip of the current branch in ORIG_HEAD , so resetting hard to it brings your index file and the working tree back to that state, and resets the tip of the branch to that commit.
Even if you may have local modifications in your working tree, you can safely say git pull when you know that the change in the other branch does not overlap with them.
After inspecting the result of the merge, you may find that the change in the other branch is unsatisfactory. Running git reset --hard ORIG_HEAD will let you go back to where you were, but it will discard your local changes, which you do not want. git reset --merge keeps your local changes.
Suppose you are interrupted by an urgent fix request while you are in the middle of a large change. The files in your working tree are not in any shape to be committed yet, but you need to get to the other branch for a quick bugfix.
This commit will get blown away so a throw-away log message is OK.
This removes the WIP commit from the commit history, and sets your working tree to the state just before you made that snapshot.
At this point the index file still has all the WIP changes you committed as snapshot WIP. This updates the index to show your WIP files as uncommitted.
Suppose you have added a file to your index, but later decide you do not want to add it to your commit. You can remove the file from the index while keeping your changes with git reset.
This removes the file from the index while keeping it in the working directory.
This commits all other changes in the index.
Adds the file to the index again.
Suppose you are working on something and you commit it, and then you continue working a bit more, but now you think that what you have in your working tree should be in another branch that has nothing to do with what you committed previously. You can start a new branch and reset it while keeping the changes in your working tree.
This commits your first edits in branch1 .
In the ideal world, you could have realized that the earlier commit did not belong to the new topic when you created and switched to branch2 (i.e. git switch -c branch2 start ), but nobody is perfect.
But you can use reset --keep to remove the unwanted commit after you switched to branch2 .
Suppose that you have created lots of logically separate changes and committed them together. Then, later you decide that it might be better to have each logical chunk associated with its own commit. You can use git reset to rewind history without changing the contents of your local files, and then successively use git add -p to interactively select which hunks to include into each commit, using git commit -c to pre-populate the commit message.
First, reset the history back one commit so that we remove the original commit, but leave the working tree with all the changes. The -N ensures that any new files added with HEAD are still marked so that git add -p will find them.
Next, we interactively select diff hunks to add using the git add -p facility. This will ask you about each diff hunk in sequence and you can use simple commands such as "yes, include this", "No don’t include this" or even the very powerful "edit" facility.
Once satisfied with the hunks you want to include, you should verify what has been prepared for the first commit by using git diff --cached . This shows all the changes that have been moved into the index and are about to be committed.
Next, commit the changes stored in the index. The -c option specifies to pre-populate the commit message from the original message that you started with in the first commit. This is helpful to avoid retyping it. The HEAD@ is a special notation for the commit that HEAD used to be at prior to the original reset commit (1 change ago). See git-reflog[1] for more details. You may also use any other valid commit reference.
You can repeat steps 2-4 multiple times to break the original code into any number of commits.
Now you’ve split out many of the changes into their own commits, and might no longer use the patch mode of git add , in order to select all remaining uncommitted changes.
Once again, check to verify that you’ve included what you want to. You may also wish to verify that git diff doesn’t show any remaining changes to be committed later.
В этом разделе мы обсудим доступные стратегии и команды Git для выполнения отмены изменений. Прежде всего необходимо отметить, что в Git не существует традиционной системы отмены, как в текстовых редакторах. Лучше воздержаться от сопоставления операций Git с какой бы то ни было традиционной концепцией отмены изменений. Кроме того, Git имеет собственную систему терминов для операций отмены, и в обсуждении лучше всего использовать их. В числе таких терминов — сброс (reset), возврат (revert), переключение (checkout), очистка (clean) и другие.
Git можно рассматривать как инструмент для управления временной шкалой. Коммиты — это снимки моментов времени или точек интереса на временной шкале истории проекта. Кроме того, с помощью веток можно управлять несколькими временными шкалами. Когда вы выполняете операцию отмены в Git, вы, как правило, перемещаетесь назад во времени или на другую временную шкалу, где ошибок не было.
Это руководство предоставляет все необходимые навыки для работы с предыдущими версиями проекта по разработке программного обеспечения. Сначала мы рассмотрим, как исследовать старые коммиты, а затем изучим разницу между отменой публичных коммитов в истории проекта и сбросом неопубликованных изменений на локальном компьютере.
Unstaging a Staged File
The next two sections demonstrate how to work with your staging area and working directory changes. The nice part is that the command you use to determine the state of those two areas also reminds you how to undo changes to them. For example, let’s say you’ve changed two files and want to commit them as two separate changes, but you accidentally type git add * and stage them both. How can you unstage one of the two? The git status command reminds you:
Right below the “Changes to be committed” text, it says use git reset HEAD … to unstage. So, let’s use that advice to unstage the CONTRIBUTING.md file:
The command is a bit strange, but it works. The CONTRIBUTING.md file is modified but once again unstaged.
It’s true that git reset can be a dangerous command, especially if you provide the --hard flag. However, in the scenario described above, the file in your working directory is not touched, so it’s relatively safe.
For now this magic invocation is all you need to know about the git reset command. We’ll go into much more detail about what reset does and how to master it to do really interesting things in Reset Demystified.
Отмена последнего коммита
Отмена коммита с помощью git checkout
С помощью команды git checkout мы можем перейти к предыдущему коммиту , a1e8fb5, и вернуть репозиторий в состояние, предшествовавшее этому безумному коммиту. Переход к отдельному коммиту переведет репозиторий в состояние открепленного указателя HEAD . Работа при этом перестает принадлежать какой-либо из веток. При открепленном указателе HEAD все новые коммиты будут оставаться без родителя, пока вы не вернете ветки в положенное состояние. «Сборщик мусора» в Git удаляет коммиты без родителя. Этот сервис работает с определенными интервалами и удаляет такие коммиты без возможности восстановления. Чтобы такие коммиты не были удалены «сборщиком мусора», перед их выполнением нужно убедиться, что мы работаем в ветке.
При наличии открепленного указателя HEAD можно выполнить команду git checkout -b new_branch_without_crazy_commit . Она создаст новую ветку с именем new_branch_without_crazy_commit и совершит переход в это состояние. Теперь репозиторий находится на новой временной шкале, где коммита 872fa7e не существует. На этом этапе мы можем продолжить работу в новой ветке, где коммита 872fa7e не существует и его можно считать «отмененным». К сожалению, если вам нужна предыдущая ветка (возможно, это главная ветка main ), такая стратегия не подходит. Поэтому рассмотрим другие стратегии отмены. Более детальную информацию и примеры см. в нашей подробной статье о git checkout .
OPTIONS
Be quiet, only report errors.
Refresh the index after a mixed reset. Enabled by default.
Pathspec is passed in instead of commandline args. If is exactly - then standard input is used. Pathspec elements are separated by LF or CR/LF. Pathspec elements can be quoted as explained for the configuration variable core.quotePath (see git-config[1]). See also --pathspec-file-nul and global --literal-pathspecs .
Only meaningful with --pathspec-from-file . Pathspec elements are separated with NUL character and all other characters are taken literally (including newlines and quotes).
Do not interpret any more arguments as options.
Limits the paths affected by the operation.
For more details, see the pathspec entry in gitglossary[7].
Резюме
Мы рассмотрели множество общих стратегий отмены изменений в Git. Важно помнить, что отменить изменения в проекте Git можно несколькими способами. Кроме того, здесь были затронуты и более сложные темы, которые подробно рассматриваются на страницах, посвященных соответствующим командам Git. Наиболее часто используемые инструменты для отмены — это команды git checkout, git revert и git reset . Вот несколько ключевых моментов, о которых следует помнить.
- Обычно после коммита внесенные изменения отменить невозможно
- Используйте git checkout для переходов и просмотра истории коммитов
- Команда git revert — лучший инструмент для отмены общих публичных изменений.
- Команду git reset лучше всего использовать для отмены локальных частных изменений.
Помимо основных команд отмены, мы рассмотрели другие команды Git: git log для поиска потерянных коммитов, git clean для отмены изменений, не подтвержденных коммитами, и git add для изменения индексирования.
Каждая из этих команд имеет собственную подробную документацию. Чтобы узнать больше о той или иной команде, пройдите по соответствующим ссылкам.
Отмена индексации файла
Следующие два раздела демонстрируют как работать с индексом и изменениями в рабочем каталоге. Радует, что команда, которой вы определяете состояние этих областей, также подсказывает вам как отменять изменения в них. Например, вы изменили два файла и хотите добавить их в разные коммиты, но случайно выполнили команду git add * и добавили в индекс оба. Как исключить из индекса один из них? Команда git status напомнит вам:
Прямо под текстом «Changes to be committed» говорится: используйте git reset HEAD … для исключения из индекса. Давайте последуем этому совету и отменим индексирование файла CONTRIBUTING.md :
Команда выглядит несколько странно, но — работает! Файл CONTRIBUTING.md изменен, но больше не добавлен в индекс.
Команда git reset может быть опасной если вызвать её с параметром --hard . В приведенном примере файл не был затронут, следовательно команда относительно безопасна.
На текущий момент этот магический вызов — всё, что вам нужно знать о команде git reset . Мы рассмотрим в деталях что именно делает reset и как с её помощью делать действительно интересные вещи в разделе Раскрытие тайн reset главы 7.
Отмена действий с помощью git restore
Git версии 2.23.0 представил новую команду: git restore . По сути, это альтернатива git reset, которую мы только что рассмотрели. Начиная с версии 2.23.0, Git будет использовать git restore вместо git reset для многих операций отмены.
Давайте проследим наши шаги и отменим действия с помощью git restore вместо git reset .
Отмена индексации файла с помощью git restore
В следующих двух разделах показано, как работать с индексом и изменениями рабочей копии с помощью git restore . Приятно то, что команда, которую вы используете для определения состояния этих двух областей, также напоминает вам, как отменить изменения в них. Например, предположим, что вы изменили два файла и хотите зафиксировать их как два отдельных изменения, но случайно набираете git add * и индексируете их оба. Как вы можете убрать из индекса один из двух? Команда git status напоминает вам:
Прямо под текстом «Changes to be committed», написано использовать git restore --staged … для отмены индексации файла. Итак, давайте воспользуемся этим советом, чтобы убрать из индекса файл CONTRIBUTING.md :
Файл CONTRIBUTING.md изменен, но снова не индексирован.
Откат измененного файла с помощью git restore
Что, если вы поймете, что не хотите сохранять изменения в файле CONTRIBUTING.md ? Как легко его откатить — вернуть обратно к тому, как он выглядел при последнем коммите (или изначально клонирован, или каким-либо образом помещён в рабочий каталог)? К счастью, git status тоже говорит, как это сделать. В выводе последнего примера, неиндексированная область выглядит следующим образом:
Он довольно недвусмысленно говорит, как отменить сделанные вами изменения. Давайте сделаем то, что написано:
Важно понимать, что git restore — опасная команда. Любые локальные изменения, внесенные в этот файл, исчезнут — Git просто заменит файл последней зафиксированной версией. Никогда не используйте эту команду, если точно не знаете, нужны ли вам эти несохраненные локальные изменения.
At any stage, you may want to undo something. Here, we’ll review a few basic tools for undoing changes that you’ve made. Be careful, because you can’t always undo some of these undos. This is one of the few areas in Git where you may lose some work if you do it wrong.
One of the common undos takes place when you commit too early and possibly forget to add some files, or you mess up your commit message. If you want to redo that commit, make the additional changes you forgot, stage them, and commit again using the --amend option:
This command takes your staging area and uses it for the commit. If you’ve made no changes since your last commit (for instance, you run this command immediately after your previous commit), then your snapshot will look exactly the same, and all you’ll change is your commit message.
The same commit-message editor fires up, but it already contains the message of your previous commit. You can edit the message the same as always, but it overwrites your previous commit.
As an example, if you commit and then realize you forgot to stage the changes in a file you wanted to add to this commit, you can do something like this:
You end up with a single commit — the second commit replaces the results of the first.
It’s important to understand that when you’re amending your last commit, you’re not so much fixing it as replacing it entirely with a new, improved commit that pushes the old commit out of the way and puts the new commit in its place. Effectively, it’s as if the previous commit never happened, and it won’t show up in your repository history.
The obvious value to amending commits is to make minor improvements to your last commit, without cluttering your repository history with commit messages of the form, “Oops, forgot to add a file” or “Darn, fixing a typo in last commit”.
Only amend commits that are still local and have not been pushed somewhere. Amending previously pushed commits and force pushing the branch will cause problems for your collaborators. For more on what happens when you do this and how to recover if you’re on the receiving end read The Perils of Rebasing.
Разница между командами git reset и git revert
Важно понимать, что команда git revert отменяет одиночный коммит, но не возвращает проект в предшествовавшее состояние с удалением всех последующих коммитов. Такой результат в Git дает команда reset, а не revert.
У команды revert есть два серьезных преимущества перед командой reset. Во-первых, она не изменяет историю проекта и, соответственно, безопасна для коммитов, которые уже опубликованы в общем репозитории. Подробнее о том, почему изменять общую историю опасно, см. на странице команды git reset.
Во-вторых, целью для команды git revert можно выбрать любой отдельный коммит в истории проекта, в то время как действие команды git reset может отменить только коммит, предшествующий текущему. Если бы возникла необходимость отменить старый коммит с помощью git reset , пришлось бы удалить все коммиты после целевого, затем удалить его и выполнить все последующие коммиты заново. Очевидно, это не лучший способ отмены. Подробные сведения о различиях между командой git revert и другими командами отмены см. на странице Команды reset, checkout и revert.
Просмотр старых версий
В этом примере предполагается, что вы начали разработку безумного эксперимента, но не уверены, хотите его сохранить или нет. Чтобы принять решение, вы хотите взглянуть на состояние проекта до начала эксперимента. Прежде всего, нужно найти идентификатор редакции, которую вы хотите просмотреть.
Допустим, история вашего проекта выглядит примерно так:
Для просмотра коммита «Make some important changes to hello.txt» можно использовать команду git checkout в следующем виде:
Это приведет к тому, что ваш рабочий каталог будет в точности соответствовать состоянию коммита a1e8fb5 . Вы можете просматривать файлы, компилировать проект, запускать тесты и даже редактировать файлы, не боясь потерять текущее состояние проекта. Никакие внесенные здесь изменения не будут сохранены в репозитории. Чтобы продолжить разработку, необходимо вернуться к текущему состоянию проекта:
Предположим, вы ведете разработку в главной ветке main по умолчанию. При каждом возвращении в ветку main можно использовать команду git revert или git reset для отмены нежелательных изменений.
Undoing things with git restore
Git version 2.23.0 introduced a new command: git restore . It’s basically an alternative to git reset which we just covered. From Git version 2.23.0 onwards, Git will use git restore instead of git reset for many undo operations.
Let’s retrace our steps, and undo things with git restore instead of git reset .
Unstaging a Staged File with git restore
The next two sections demonstrate how to work with your staging area and working directory changes with git restore . The nice part is that the command you use to determine the state of those two areas also reminds you how to undo changes to them. For example, let’s say you’ve changed two files and want to commit them as two separate changes, but you accidentally type git add * and stage them both. How can you unstage one of the two? The git status command reminds you:
Right below the “Changes to be committed” text, it says use git restore --staged … to unstage. So, let’s use that advice to unstage the CONTRIBUTING.md file:
The CONTRIBUTING.md file is modified but once again unstaged.
Unmodifying a Modified File with git restore
What if you realize that you don’t want to keep your changes to the CONTRIBUTING.md file? How can you easily unmodify it — revert it back to what it looked like when you last committed (or initially cloned, or however you got it into your working directory)? Luckily, git status tells you how to do that, too. In the last example output, the unstaged area looks like this:
It tells you pretty explicitly how to discard the changes you’ve made. Let’s do what it says:
It’s important to understand that git restore is a dangerous command. Any local changes you made to that file are gone — Git just replaced that file with the last staged or committed version. Don’t ever use this command unless you absolutely know that you don’t want those unsaved local changes.
In the first three forms, copy entries from to the index. In the last form, set the current branch head ( HEAD ) to , optionally modifying index and working tree to match. The / defaults to HEAD in all forms.
These forms reset the index entries for all paths that match the to their state at . (It does not affect the working tree or the current branch.)
This means that git reset is the opposite of git add . This command is equivalent to git restore [--source=] --staged . .
After running git reset to update the index entry, you can use git-restore[1] to check the contents out of the index to the working tree. Alternatively, using git-restore[1] and specifying a commit with --source , you can copy the contents of a path out of a commit to the index and to the working tree in one go.
Interactively select hunks in the difference between the index and (defaults to HEAD ). The chosen hunks are applied in reverse to the index.
This means that git reset -p is the opposite of git add -p , i.e. you can use it to selectively reset hunks. See the “Interactive Mode” section of git-add[1] to learn how to operate the --patch mode.
This form resets the current branch head to and possibly updates the index (resetting it to the tree of ) and the working tree depending on . If is omitted, defaults to --mixed . The must be one of the following:
Does not touch the index file or the working tree at all (but resets the head to , just like all modes do). This leaves all your changed files "Changes to be committed", as git status would put it.
Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated. This is the default action.
If -N is specified, removed paths are marked as intent-to-add (see git-add[1]).
Resets the index and working tree. Any changes to tracked files in the working tree since are discarded. Any untracked files or directories in the way of writing any tracked files are simply deleted.
Resets the index and updates the files in the working tree that are different between and HEAD , but keeps those which are different between the index and working tree (i.e. which have changes which have not been added). If a file that is different between and the index has unstaged changes, reset is aborted.
In other words, --merge does something like a git read-tree -u -m , but carries forward unmerged index entries.
Resets index entries and updates files in the working tree that are different between and HEAD . If a file that is different between and HEAD has local changes, reset is aborted.
When the working tree is updated, using --recurse-submodules will also recursively reset the working tree of all active submodules according to the commit recorded in the superproject, also setting the submodules' HEAD to be detached at that commit.
See "Reset, restore and revert" in git[1] for the differences between the three commands.
Распространенные опции
Это параметр по умолчанию, указывать его необязательно. Он открывает настроенный системный редактор, в котором вам будет предложено изменить комментарий к коммиту перед выполнением отмены
Действие этого параметра обратно действию -e . Редактор во время выполнения отмены не откроется.
При передаче этого параметра команда git revert не будет создавать новый коммит, обратный целевому по своему содержанию. Вместо этого обращенные изменения будут добавлены в раздел проиндексированных файлов и рабочий каталог. Это другие деревья, с помощью которых Git управляет состоянием репозитория. Подробную информацию см. на странице команды git reset .
Поиск утерянного: просмотр старых коммитов
В основе любой системы управления версиями лежит идея хранения «безопасных» копий проекта, чтобы у разработчиков не возникало опасений безвозвратно испортить базу кода. Когда в проекте сохранена история коммитов, можно повторно оценивать и анализировать любые ранее выполненные коммиты. Один из лучших инструментов для просмотра истории репозитория Git — команда git log . В примере ниже мы используем команду git log для получения последних коммитов популярной графической библиотеки с открытым исходным кодом.
Каждый коммит имеет уникальный идентифицирующий хеш SHA-1. Эти идентификаторы используются для перемещения по временной шкале коммитов и возвращения к коммитам. По умолчанию git log показывает только коммиты текущей выбранной ветки. Но не исключено, что искомый коммит находится в другой ветке. Для просмотра всех коммитов во всех ветках используется команда git log --branches=* . Команда git branch используется для просмотра и посещения других веток. Так, команда git branch -a возвращает список имен всех известных веток. Просмотреть весь журнал коммитов одной из этих веток можно с помощью команды git log .
После того как вы нашли ссылку на нужный коммит в истории, для перехода к нему можно использовать команду git checkout . Команда git checkout — это простой способ «загрузить» любой из этих сохраненных снимков на компьютер разработчика. При стандартном процессе разработки указатель HEAD обычно указывает на главную ветку main или другую локальную ветку. Но при переключении на предыдущий коммит HEAD указывает уже не на ветку, а непосредственно на сам коммит. Такая ситуация называется состоянием открепленного указателя HEAD , и ее можно представить так:
Переход к старой версии файла не перемещает указатель HEAD . Он остается в той же ветке и в том же коммите, что позволяет избежать открепления указателя HEAD. После этого можно выполнить коммит старой версии файла в новый снимок состояния, как и в случае других изменений. Соответственно, такое использование команды git checkout применительно к файлу позволяет откатиться к прежней версии отдельного файла. Для получения дополнительной информации об этих двух режимах посетите страницу команды git checkout .
Отмена публичного коммита с помощью git revert
Предположим, мы вернулись к исходному примеру истории коммитов. Истории, в которую входит коммит 872fa7e . В этот раз попробуем отмену путем обратной операции. При исполнении команды git revert HEAD Git создаст новый коммит с операцией, обратной последнему коммиту. В текущую историю ветки будет добавлен новый коммит, и она будет выглядеть следующим образом:
На этом этапе мы снова технически «отменили» коммит 872fa7e . Хотя коммит 872fa7e по-прежнему существует в истории, новый коммит e2f9a78 отменил изменения 872fa7e . В отличие от нашей предыдущей стратегии переключения с помощью команды checkout, мы можем продолжить работать с этой же веткой, поэтому данная стратегия является удовлетворительной. Это идеальный способ отмены при работе в открытых общих репозиториях, однако если у вас есть требование вести минимальную «очищенную» историю Git, эта стратегия может не подойти.
Отмена изменений в файле
Что делать, если вы поняли, что не хотите сохранять свои изменения файла CONTRIBUTING.md ? Как можно просто отменить изменения в нём — вернуть к тому состоянию, которое было в последнем коммите (или к начальному после клонирования, или еще как-то полученному)? Нам повезло, что git status подсказывает и это тоже.
В выводе команды из последнего примера список изменений выглядит примерно так:
Здесь явно сказано как отменить существующие изменения. Давайте так и сделаем:
Как видите, откат изменений выполнен.
Важно понимать, что git checkout -- — опасная команда. Все локальные изменения в файле пропадут — Git просто заменит его версией из последнего коммита. Ни в коем случае не используйте эту команду, если вы не уверены, что изменения в файле вам не нужны.
Если вы хотите сохранить изменения в файле, но прямо сейчас их нужно отменить, то есть способы получше, такие как ветвление и припрятывание — мы рассмотрим их в главе Ветвление в Git.
Помните, все что попало в коммит почти всегда Git может восстановить. Можно восстановить даже коммиты из веток, которые были удалены, или коммиты, перезаписанные параметром --amend (см. Восстановление данных). Но всё, что не было включено в коммит и потеряно — скорее всего, потеряно навсегда.
Резюме
Команда git revert — это операция для безопасной отмены изменений, действие которой направлено в будущее. Для отката изменений команда не удаляет из истории коммиты или родительские элементы, a создает новый коммит с отменой нужных действий. Использовать команду git revert безопаснее, потому что она не создает угрозу потери кода, в отличие от git reset . Чтобы показать применение команды git revert , в примерах использовались другие команды. Подробные сведения о них см. на страницах команд git log , git commit и git reset .
В любой момент вам может потребоваться что-либо отменить. Здесь мы рассмотрим несколько основных способов отмены сделанных изменений. Будьте осторожны, не все операции отмены в свою очередь можно отменить! Это одна из редких областей Git, где неверными действиями можно необратимо удалить результаты своей работы.
Отмена может потребоваться, если вы сделали коммит слишком рано, например, забыв добавить какие-то файлы или комментарий к коммиту. Если вы хотите переделать коммит — внесите необходимые изменения, добавьте их в индекс и сделайте коммит ещё раз, указав параметр --amend :
Например, если вы сделали коммит и поняли, что забыли проиндексировать изменения в файле, который хотели добавить в коммит, то можно сделать следующее:
В итоге получится единый коммит — второй коммит заменит результаты первого.
Очень важно понимать, что когда вы вносите правки в последний коммит, вы не столько исправляете его, сколько заменяете новым, который полностью его перезаписывает. В результате всё выглядит так, будто первоначальный коммит никогда не существовал, а так же он больше не появится в истории вашего репозитория.
Отмена неотправленных изменений
Пока не выполнен коммит изменений в историю репозитория, они находятся в разделе проиндексированных файлов и в рабочем каталоге. Вам может потребоваться отменить изменения в этих двух областях. Раздел проиндексированных файлов и рабочий каталог являются внутренними механизмами управления состоянием Git. Подробную информацию о том, как работают эти два механизма, см. на странице git reset , где приводится их подробное описание.
Unmodifying a Modified File
What if you realize that you don’t want to keep your changes to the CONTRIBUTING.md file? How can you easily unmodify it — revert it back to what it looked like when you last committed (or initially cloned, or however you got it into your working directory)? Luckily, git status tells you how to do that, too. In the last example output, the unstaged area looks like this:
It tells you pretty explicitly how to discard the changes you’ve made. Let’s do what it says:
You can see that the changes have been reverted.
It’s important to understand that git checkout -- is a dangerous command. Any local changes you made to that file are gone — Git just replaced that file with the last staged or committed version. Don’t ever use this command unless you absolutely know that you don’t want those unsaved local changes.
If you would like to keep the changes you’ve made to that file but still need to get it out of the way for now, we’ll go over stashing and branching in Git Branching; these are generally better ways to go.
Remember, anything that is committed in Git can almost always be recovered. Even commits that were on branches that were deleted or commits that were overwritten with an --amend commit can be recovered (see Data Recovery for data recovery). However, anything you lose that was never committed is likely never to be seen again.
Порядок действий
Команда git revert используется для отката изменений в истории коммитов репозитория. Другие команды отмены, такие как git checkout и git reset , перемещают указатель HEAD и указатели ветки на определенный коммит. Команда git revert также работает с определенным коммитом, однако использование git revert не перемещает указатели. При операции revert совершается переход к указанному коммиту, обращаются его изменения и создается новый, «обратный» коммит. Затем позиция указателей обновляется — они перемещаются к этому коммиту в конце ветки.
Для наглядности создадим тестовый репозиторий и напишем в командной строке следующее:
Мы выполнили инициализацию репозитория в новом каталоге git_revert_test . В репозитории мы выполнили 3 коммита: добавили файл demo_file и дважды изменили его содержимое. В конце настройки репозитория мы запустили команду git log , чтобы просмотреть историю коммитов (в ней содержится 3 коммита). Теперь из этого состояния репозитория можно запустить команду git revert .
Команда git revert не будет работать, если не передана ссылка на коммит. В примере мы указали ссылку HEAD . Это позволит обратить изменения последнего коммита. То же самое произошло бы при откате до коммита 3602d8815dbfa78cd37cd4d189552764b5e96c58 . Как и слияние, операция отката создаст новый коммит. При этом откроется настроенный системный редактор с предложением ввести комментарий. После ввода и сохранения комментария Git продолжит операцию. Теперь мы можем узнать состояние репозитория с помощью команды git log . Мы видим, что к журналу добавлен новый коммит:
Обратите внимание, что после операции отмены третий коммит по-прежнему содержится в истории проекта. Вместо его удаления команда git revert добавила новый коммит для отмены изменений. В результате второй и четвертый коммиты представляют собой одну и ту же базу кода, а третий коммит хранится в истории на случай, если в дальнейшем нам понадобится к нему вернуться.
Отмена публичных изменений
При командной работе в удаленных репозиториях необходимо подходить к отмене изменений с особой осторожностью. Команда git reset , как правило, считается методом локальной отмены. Ее следует использовать для отмены изменений в частной ветке. Она безопасно изолирует удаление коммитов от других веток, которые могут использоваться другими разработчиками. Проблемы возникают, когда команда reset выполняется в общей ветке и затем эта ветка удаленно публикуется с помощью команды git push . В этом случае Git блокирует выполнение команды push и сообщает, что публикуемая ветка устарела, поскольку в ней отсутствуют коммиты, которые есть в удаленной ветке.
Предпочтительная команда для отмены общей истории коммитов — git revert . Команда revert безопаснее, чем reset, так как она не удаляет коммиты из общей истории. Команда revert сохраняет отменяемые вами коммиты и создает новый коммит с операцией, обратной последнему коммиту. Этот метод можно безопасно применять в общих распределенных рабочих средах, так как удаленный разработчик может выполнить пул ветки и получить новый коммит, который отменяет его нежелательный коммит.
Читайте также: