Есть ли способ отменить эффекты «git revert head»?
Я случайно запустил команду против неправильной ветви в моем репозитории — есть ли способ отменить это изменение?
4 ответов
git revert просто создает новую фиксацию — вы можете «удалить» ее с помощью git reset —hard HEAD^ (быть более осторожны с ним, хотя!)
команда git revert просто создает фиксацию, которая отменяет другую. Вы должны быть в состоянии работать git revert HEAD снова, и он отменит предыдущую отмену и добавит еще одну фиксацию для этого. Или вы могли бы сделать git reset —hard HEAD
. Но будьте осторожны с последним, так как он стирает данные.
означает фиксацию перед текущим глава
Как насчет возврата возврата?
Просмотр журнала git и получить хэш-тег плохого возврата:
затем отмените сам возврат:
если бы Вы были достаточно прозорливы, чтобы сделать это: revert —no-commit master , вы можете прервать это с помощью: git revert —abort на git status совет:
Полезные команды Git: безопасная отмена коммитов, добавление файла из другой ветки и другие
Git — это мощный, хотя и незамысловато выглядящий, инструмент, который при неосторожном использовании может устроить большой беспорядок. Поэтому, прежде чем пробовать выполнять различные фокусы с Git, я рекомендую ознакомиться с его основными командами (init, clone, push, pull, status, log и т.д.) и концептами (репозиторий, ветвь, коммит, и т.д.).
Итак, если вы уверенно чувствуете себя при работе с Git, вот несколько полезных трюков, о которых я знаю.
Reflog. Отмена операций
Я помню, что в начале знакомства с Git я боялся запускать команды. Я думал, что всё испорчу. Например, первое использование git rebase вызвало у меня несколько восклицаний наподобие «Вот *****! Что я только что сделал?!» из-за дублировавшихся или вообще исчезнувших коммитов. Вскоре после этого я понял, что нужно идти дальше.
Лично я не люблю использовать инструмент, не зная, что именно он делает для достижения результата. Это как вождение машины без руля: всё прекрасно до тех пор, пока не доедешь до поворота. А что потом? Ну, вы меня поняли.
Итак, в нашем деле очень важно понимать концепты. Я много читал про Git, прошёл несколько обучающих курсов, и моё видение мира Git кардинально изменилось. Используя его, я стал чувствовать себя гораздо спокойнее, а что более важно, я познал его истинную мощь. И одной из самых мощных команд является git reflog .
Используя reflog , вы можете вернуться в прошлое, отменяя почти любые действия, сделанные в Git. Даже если это были rebase или reset . Кроме того, её легко использовать. Чтобы доказать это, приведу маленький пример.
Чтобы изменить нужное состояние, нужно запустить checkout , используя абсолютную ссылку:
или относительную ссылку:
и вы окажетесь в независимом (detached) состоянии HEAD, из которого можно создать новую ветвь.
Вот и всё. Все операции после вышеуказанного состояния отменены.
Revert. Отмена изменений коммита
Вы, должно быть, сталкивались с ситуацией, когда вам нужно было отменить некоторые изменения. На помощь придёт команда revert :
Что она делает? Она просто отменяет действия прошлых коммитов, создавая новый, содержащий все отменённые изменения. Зачем использовать её вместо других решений? Это — единственный безопасный способ, так как он не изменяет историю коммитов. Он обычно используется в публичных ветвях, где изменение истории нежелательно.
Давайте взглянем на другие команды, способные отменять действия.
Rebase
Вы можете выполнить эту команду и просто убрать строки, относящиеся к ненужным коммитам. Это самое очевидное и простое решение, но у него есть недостаток, из-за которого его нельзя использовать в публичных ветвях: он изменяет историю коммитов. Поэтому после rebase’а у вас может возникнуть проблема с push’ем.
Reset
Эта команда удаляет все коммиты выше указанного. Она лучше предыдущей тем, что не добавляет новую информацию в историю, но тем не менее тоже ее меняет.
Checkout
Эта команда откатывает выбранный файл к состоянию в более ранней версии указанного коммита. Отменённые изменения будут в рабочей директории, и их можно будет закоммитить как угодно.
Вывод таков, что git revert — самая безопасная операция для отмены действий. Кроме того, нужно заметить, что эти команды не взаимозаменяемы в 100% случаев.
Log. Более приятное форматирование
С git log всё просто. Вы вводите команду и видите историю коммитов в хронологическом порядке, и каждый пункт включает в себя хэш коммита, автора, дату и прикреплённое сообщение.
Давайте взглянем на вид лога по умолчанию:
Я думаю, что могло бы быть и лучше.
Я предпочитаю простые вещи. Поэтому я использую две опции:
- —oneline – показывает каждый коммит в одной строке. Кроме того, она показывает лишь префикс ID коммита.
- —decorate – печатает все относительные имена показанных коммитов.
Вот как это выглядит:
Опция —oneline сокращает количество занятых строк, а опция —decorate добавляет относительные имена (локальные/удалённые ветви + HEAD). Используя эти две опции, мы получаем более компактный и осмысленный git log . Да, мы теряем некоторые вещи, но они не особо важны. Так гораздо лучше, да?
Тем не менее, иногда нам нужно знать дату и автора коммита. В таких случаях я использую следующее:
Ничего себе! Давайте посмотрим, как это выглядит:
И всё-таки вводить эти команды каждый раз достаточно неудобно, так? Давайте создадим алиасы. Я считаю, что использование алиасов очень сильно повышает продуктивность. Чтобы зарегистрировать их, добавим следующие строки в «
/.bash_profile» (конечно, если вы используете *nix OS):
Таким образом, мы получили не только хорошо выглядящую историю коммитов, но и упрощённый способ доступа к ней, используя команды gl и gh .
Еще один вариант украшения вывода лога приведен в этой статье.
Diff. Разница между коммитами
Простая команда, показывающая изменения между объектами в проекте — git diff . Один из основных сценариев её использования — сравнение двух коммитов:
Другой — вывод всех изменений, не внесённых в индекс. Для этого нужно выполнить следующее:
Branch. Проверка ветвей на слияние
Я думаю, вы уже знаете, что делает базовая команда git branch (без опций выводит список локальных веток — прим. перев.), поэтому перейдём к делу. Время от времени вам понадобится удалять старые или ненужные ветви из вашего «дерева» git.
вы получите список всех веток, соединённых с текущей.
Можно и наоборот:
Так вы получите список всех не соединённых с текущей веток.
Таким образом, вы всегда узнаете, безопасно ли удаление той или иной ветви.
Checkout. Получение файла из другой ветви
Вам наверняка приходилось искать конкретную версию файла, которая была в другой, ещё не присоединённой ветви. Помните команду checkout , приведённую выше? Она выглядит так:
Указанный файл будет приведен к версии, указанной в заданном коммите. Раньше мы использовали это для отмены изменений файла. Удобно то, что этот коммит необязательно должен находиться в той же ветви, что и наш файл. Поэтому мы можем задать любому файлу любую версию из любого коммита или ветви.
Надеюсь, вы нашли здесь для себя что-то полезное. Прошу заметить, что все вышеуказанные моменты субъективны, поскольку я подгонял их под свои нужды.
How to undo (almost) anything with Git
One of the most useful features of any version control system is the ability to «undo» your mistakes. In Git, «undo» can mean many slightly different things.
One of the most useful features of any version control system is the ability to “undo” your mistakes. In Git, “undo” can mean many slightly different things.
When you make a new commit, Git stores a snapshot of your repository at that specific moment in time; later, you can use Git to go back to an earlier version of your project.
In this post, I’m going to take a look at some common scenarios where you might want to “undo” a change you’ve made and the best way to do it using Git.
Undo a “public” change
Scenario: You just ran git push , sending your changes to GitHub, now you realize there’s a problem with one of those commits. You’d like to undo that commit.
Undo with: git revert <SHA>
What’s happening: git revert will create a new commit that’s the opposite (or inverse) of the given SHA. If the old commit is “matter”, the new commit is “anti-matter”—anything removed in the old commit will be added in the new commit and anything added in the old commit will be removed in the new commit.
This is Git’s safest, most basic “undo” scenario, because it doesn’t alter history—so you can now git push the new “inverse” commit to undo your mistaken commit.
Fix the last commit message
Scenario: You just typo’d the last commit message, you did git commit -m «Fxies bug #42» but before git push you realized that really should say “Fixes bug #42”.
Undo with: git commit —amend or git commit —amend -m «Fixes bug #42»
What’s happening: git commit —amend will update and replace the most recent commit with a new commit that combines any staged changes with the contents of the previous commit. With nothing currently staged, this just rewrites the previous commit message.
Undo “local” changes
Scenario: The cat walked across the keyboard and somehow saved the changes, then crashed the editor. You haven’t committed those changes, though. You want to undo everything in that file—just go back to the way it looked in the last commit.
Undo with: git checkout — <bad filename>
What’s happening: git checkout alters files in the working directory to a state previously known to Git. You could provide a branch name or specific SHA you want to go back to or, by default, Git will assume you want to checkout HEAD , the last commit on the currently-checked-out branch.
Keep in mind: any changes you “undo” this way are really gone. They were never committed, so Git can’t help us recover them later. Be sure you know what you’re throwing away here! (Maybe use git diff to confirm.)
Reset “local” changes
Scenario: You’ve made some commits locally (not yet pushed), but everything is terrible, you want to undo the last three commits—like they never happened.
Undo with: git reset <last good SHA> or git reset —hard <last good SHA>
What’s happening: git reset rewinds your repository’s history all the way back to the specified SHA. It’s as if those commits never happened. By default, git reset preserves the working directory. The commits are gone, but the contents are still on disk. This is the safest option, but often, you’ll want to “undo” the commits and the changes in one move—that’s what —hard does.
Redo after undo “local”
Scenario: You made some commits, did a git reset —hard to “undo” those changes (see above), and then realized: you want those changes back!
Undo with: git reflog and git reset or git checkout
What’s happening: git reflog is an amazing resource for recovering project history. You can recover almost anything—anything you’ve committed—via the reflog.
You’re probably familiar with the git log command, which shows a list of commits. git reflog is similar, but instead shows a list of times when HEAD changed.
- HEAD changes only. HEAD changes when you switch branches, make commits with git commit and un-make commits with git reset , but HEAD does not change when you git checkout — <bad filename> (from an earlier scenario—as mentioned before, those changes were never committed, so the reflog can’t help us recover those.
- git reflog doesn’t last forever. Git will periodically clean up objects which are “unreachable.” Don’t expect to find months-old commits lying around in the reflog forever.
- Your reflog is yours and yours alone. You can’t use git reflog to restore another developer’s un-pushed commits.
So… how do you use the reflog to “redo” a previously “undone” commit or commits? It depends on what exactly you want to accomplish:
- If you want to restore the project’s history as it was at that moment in time use git reset —hard <SHA>
- If you want to recreate one or more files in your working directory as they were at that moment in time, without altering history use git checkout <SHA> — <filename>
- If you want to replay exactly one of those commits into your repository use git cherry-pick <SHA>
Once more, with branching
Scenario: You made some commits, then realized you were checked out on master . You wish you could make those commits on a feature branch instead.
Undo with: git branch feature , git reset —hard origin/master , and git checkout feature
What’s happening: You may be used to creating new branches with git checkout -b <name> —it’s a popular short-cut for creating a new branch and checking it out right away—but you don’t want to switch branches just yet. Here, git branch feature creates a new branch called feature pointing at your most recent commit, but leaves you checked out to master .
Next, git reset —hard rewinds master back to origin/master , before any of your new commits. Don’t worry, though, they are still available on feature .
Finally, git checkout switches to the new feature branch, with all of your recent work intact.
Branch in time saves nine
Scenario: You started a new branch feature based on master , but master was pretty far behind origin/master . Now that master branch is in sync with origin/master , you wish commits on feature were starting now, instead of being so far behind.
Undo with: git checkout feature and git rebase master
What’s happening: You could have done this with git reset (no —hard , intentionally preserving changes on disk) then git checkout -b <new branch name> and then re-commit the changes, but that way, you’d lose the commit history. There’s a better way.
git rebase master does a couple of things:
- First it locates the common ancestor between your currently-checked-out branch and master .
- Then it resets the currently-checked-out branch to that ancestor, holding all later commits in a temporary holding area.
- Then it advances the currently-checked-out-branch to the end of master and replays the commits from the holding area after master ‘s last commit.
Mass undo/redo
Scenario: You started this feature in one direction, but mid-way through, you realized another solution was better. You’ve got a dozen or so commits, but you only want some of them. You’d like the others to just disappear.
Undo with: git rebase -i <earlier SHA>
What’s happening: -i puts rebase in “interactive mode”. It starts off like the rebase discussed above, but before replaying any commits, it pauses and allows you to gently modify each commit as it’s replayed.
rebase -i will open in your default text editor, with a list of commits being applied, like this:
The first two columns are key: the first is the selected command for the commit identified by the SHA in the second column. By default, rebase -i assumes each commit is being applied, via the pick command.
To drop a commit, just delete that line in your editor. If you no longer want the bad commits in your project, you can delete lines 1 and 3-4 above.
If you want to preserve the contents of the commit but edit the commit message, you use the reword command. Just replace the word pick in the first column with the word reword (or just r ). It can be tempting to rewrite the commit message right now, but that won’t work— rebase -i ignores everything after the SHA column. The text after that is really just to help us remember what 0835fe2 is all about. When you’ve finished with rebase -i , you’ll be prompted for any new commit messages you need to write.
If you want to combine two commits together, you can use the squash or fixup commands, like this:
squash and fixup combine “up”—the commit with the “combine” command will be merged into the commit immediately before it. In this scenario, 0835fe2 and 6943e85 will be combined into one commit, then 38f5e4e and af67f82 will be combined together into another.
When you select squash , Git will prompt us to give the new, combined commit a new commit message; fixup will give the new commit the message from the first commit in the list. Here, you know that af67f82 is an “ooops” commit, so you’ll just use the commit message from 38f5e4e as is, but you’ll write a new message for the new commit you get from combining 0835fe2 and 6943e85 .
When you save and exit your editor, Git will apply your commits in order from top to bottom. You can alter the order commits apply by changing the order of commits before saving. If you’d wanted, you could have combined af67f82 with 0835fe2 by arranging things like this:
Fix an earlier commit
Scenario: You failed to include a file in an earlier commit, it’d be great if that earlier commit could somehow include the stuff you left out. You haven’t pushed, yet, but it wasn’t the most recent commit, so you can’t use commit —amend .
Undo with: git commit —squash <SHA of the earlier commit> and git rebase —autosquash -i <even earlier SHA>
What’s happening: git commit —squash will create a new commit with a commit message like squash! Earlier commit . (You could manually create a commit with a message like that, but commit —squash saves you some typing.)
You can also use git commit —fixup if you don’t want to be prompted to write a new commit message for the combined commit. In this scenario, you’d probably use commit —fixup , since you just want to use the earlier commit’s commit message during rebase .
rebase —autosquash -i will launch an interactive rebase editor, but the editor will open with any squash! and fixup! commits already paired to the commit target in the list of commits, like so:
When using —squash and —fixup , you might not remember the SHA of the commit you want to fix—only that it was one or five commits ago. You might find using Git’s ^ and
operators especially handy. HEAD^ is one commit before HEAD . HEAD
4 is four commits before HEAD – or, altogether, five commits back.
Stop tracking a tracked file
Scenario: You accidentally added application.log to the repository, now every time you run the application, Git reports there are unstaged changes in application.log . You put *.log in the .gitignore file, but it’s still there—how do you tell git to to “undo” tracking changes in this file?
Undo with: git rm —cached application.log
What’s happening: While .gitignore prevents Git from tracking changes to files or even noticing the existence of files it’s never tracked before, once a file has been added and committed, Git will continue noticing changes in that file. Similarly, if you’ve used git add -f to “force”, or override, .gitignore , Git will keep tracking changes. You won’t have to use -f to add it in the future.
If you want to remove that should-be-ignored file from Git’s tracking, git rm —cached will remove it from tracking but leave the file untouched on disk. Since it’s now being ignored, you won’t see that file in git status or accidentally commit changes from that file again.
That’s how to undo anything with Git. To learn more about any of the Git commands used here, check out the relevant documentation:
Как отменить коммит в Git
Система управления версиями git очень популярна среди разработчиков программного обеспечения. Она очень сильно облегчает работу в команде, разворачивание проектов на сервере, ну и конечно же управление историей изменений. Изменения в Git сохраняются порциями — коммитами. Каждый коммит содержит набор изменений, к которым в любой момент можно вернуться, и продолжить разработку с этого момента или же просто посмотреть что и когда было изменено.
Иногда случаются ситуации, что вы закомитили что-то не то, не туда или не так. Такой коммит надо удалить или отменить. В этой небольшой статье мы рассмотрим как отменить коммит Git. Обратите внимание, что если вам надо внести изменения, то коммит не обязательно отменять, можно его поправить. Но об этом в следующей статье.
Как отменить коммит в Git
Бывает, что вы что-то закомитили, а потом решили, что часть изменений вносить не надо было или в коммит попали лишние файлы. Тогда можно отменить коммит и сделать его уже заново, но без ненужных данных. Сначала желательно посмотреть историю коммитов и текущий коммит, для того чтобы не откатить ничего лишнего. Для этого выполните:
Команда вернет список коммитов с их описанием и идентификаторами (хешами), которые можно использовать для того чтобы посмотреть подробную информацию о коммите с помощью команды show. По умолчанию команда показывает изменения в последнем коммите:
Теперь можно использовать идентификатор коммита для того чтобы его отменить.
1. Отменить коммит, но оставить изменения
Для того чтобы отменить последний коммит git без удаления изменений используется команда reset с параметром —soft. Команде надо передать идентификатор коммита или его позицию относительно HEAD. В терминологии git термин HEAD — это самая последняя версия проекта в текущей ветке. С помощью HEAD можно ссылаться на коммиты в истории. Для этого используется символ
. Таким образом ссылка на предыдущий коммит будет выглядеть как HEAD
или HEAD
1, а на коммит перед ним — HEAD
2 и так далее. Для отмены последнего коммита достаточно выполнить команду:
git reset —soft HEAD
Как видите, все файлы сохранились, а если посмотреть отличия HEAD и текущего состояния проекта, то будет видно, добавление файла file3:
Аналогичного результата можно добиться, передав идентификатор коммита, например, давайте отменим коммит, добавляющий file2. Для этого посмотрите идентификатор коммита перед ним, в данном случае, это «Inital Commit» с помощью следующей команды:
А затем передайте его в команду git reset. Например:
git reset —soft 887080eea5fd8bd3bc2503dcf043ac6f5c19a8e5
И снова все файлы на месте, а в HEAD теперь будет добавлено два файла: file2 и file3:
Таким образом вы можете отменить несколько коммитов за раз, надо только указать идентификатор самого раннего коммита.
Обратите внимание, что файлы, которые ранее были в коммите, сейчас всё ещё добавлены в индекс, поэтому вам не надо вызывать git add, можно сразу создавать новый коммит. Но у команды reset есть ещё одна опция: —mixed. Она используется по умолчанию. При использовании этой опции ваши изменения тоже сохраняются, но перед следующим коммитом их снова надо будет добавить в индекс с помощью git add. При выполнении команды git status эти файлы будут отображаться как не отслеживаемые:
2. Отменить коммит и удалить изменения
Отмена коммита git с удалением изменений работает аналогично. Только здесь необходимо вместо опции —soft указывать опцию —hard. Например, при той же структуре коммитов, можно удалить последний коммит с добавлением файла file3 вместе с этим файлом:
git reset —hard HEAD
Теперь файла нет. Аналогично, вы можете указать идентификатор коммита, до которого надо отменить коммиты. Обратите внимание, что указывается не тот коммит, который надо отменить, а коммит перед ним. Ещё важно отметить, что это всё работает пока вы не отправили свои коммиты в удалённый репозиторий. Если коммиты уже отправлены, их идентификаторы сохранены там, а поэтому менять их нельзя, иначе могут возникнуть конфликты слияния, которые будет сложно решить. Теперь вы знаете отменить последний локальный коммит git.
3. Как вернуть отмененный коммит
Если вы всё же удалили что-то нужное с помощью команды reset —hard, и вовремя об этом вспомнили, то можно попытаться вернуть потерянные данные. Первый способ будет работать если вы ещё ничего не комитили после отмены комита. Для того чтобы посмотреть историю добавления/удаления коммитов используйте команду:
Затем, для того чтобы вернуться к нужному удалённому коммиту надо использовать ту же команду reset —hard со ссылкой на удалённый коммит, полученной из предыдущей команды, в виде HEAD . Например, для коммита c файлом file3 это будет выглядеть так:
git reset —hard HEAD@
Аналогично можно использовать адрес:
git reset —hard fc1f295
Срок хранения удалённых коммитов ограничен. Время от времени git удаляет мусор, так что если ждать слишком долго, то нужных данных уже может и не быть. Но найти удалённые коммиты, если git их ещё не удалил можно с помощью такой команды:
git fsck —lost-found
Затем просто используйте идентификатор коммита для того чтобы посмотреть какие в нём были изменения:
git show 8a996dd76fbacb05a2df91c0f2d19b1a3afd8451
Затем можно переключиться на этот коммит с помощью команды:
git rebase 8a996dd76fbacb05a2df91c0f2d19b1a3afd8451
Это всё тоже безопасно делать только с коммитами, ещё не отправленными в удалённый репозиторий.
4. Отменить изменения но не отменять коммит
Если вы уже отправили коммит в удалённый репозиторий, удалять его не желательно, потому что если кто-то успеет скачать репозиторий до отмены коммита, то потом у него возникнут проблемы. Однако изменения, сделанные в коммите можно отменить новым коммитом. Для того чтобы не делать это вручную существует специальная команда revert. Её уже нужно передать идентификатор именно того коммита, изменения из которого надо отменить. Для этого сначала найдите хэш коммита:
Затем выполните команду revert, например:
git revert 8a996dd76fbacb05a2df91c0f2d19b1a3afd8451
Команда предложит вам написать сообщение для отменяющего коммита, можно просто закрыть этот файл:
Затем изменения, которые были в коммите исчезнут и уже это можно будет снова пушить в удалённые репозиторий.
Выводы
В этой небольшой статье мы рассмотрели как отменить коммит git с сохранением изменений или без них. Как видите всё довольно просто. Будьте осторожны, и не сотрите ничего лишнего, чтобы не создать проблем коллегам и себе.
Обнаружили ошибку в тексте? Сообщите мне об этом. Выделите текст с ошибкой и нажмите Ctrl+Enter.