Обложка статьи «Как это отменить?! Git-команды для исправления своих ошибок»

Как это отменить?! Git-команды для исправления своих ошибок

Перевод статьи «Oh Shit, Git!?!»

Если вы ошиблись в Git’е, разобраться, что происходит и как это исправить, — непростая задача. Документация Git — это кроличья нора, из которой вы вылезете только зная конкретное название команды, которая решит вашу проблему.

Рассказываем о командах, которые помогут вам выбраться из проблемных ситуаций.

Вот блин, я сделал что-то не то… У Git ведь есть машина времени?!

git reflog
# Тут вы увидите всё, что вы делали
# в Git во всех ветках.
# У каждого элемента есть индекс HEAD@{index}.
# Найдите тот, после которого всё сломалось.
git reset HEAD@{index}
# Машина времени к вашим услугам.

Так вы можете восстановить то, что случайно удалили, и откатить слияние, после которого всё сломалось. reflog используется очень часто — давайте поблагодарим того, кто предложил добавить его в Git.

Я только что сделал коммит и заметил, что нужно кое-что поправить!

# Внесите изменения
git add . # или добавьте файлы по отдельности.
git commit --amend --no-edit
# Теперь последний коммит содержит ваши изменения.
# ВНИМАНИЕ! Никогда не изменяйте опубликованные коммиты.

Обычно эта команда нужна если вы что-то закоммитили, а потом заметили какую-то мелочь, например отсутствующий пробел после знака =. Конечно вы можете внести изменения новым коммитом, а потом объединить коммиты с помощью rebase -i, но это гораздо дольше.

Внимание Никогда не изменяйте коммиты в публичной ветке. Используйте эту команду только для коммитов в локальной ветке, иначе вам конец.

Мне нужно изменить сообщение последнего коммита!

git commit --amend
# Открывает редактор сообщений коммита.

Тупые требования к оформлению сообщений…

Я случайно закоммитил что-то в мастер, хотя должен был в новую ветку!

# Эта команда создаст новую ветку из текущего состояния мастера.
git branch some-new-branch-name
# А эта — удалит последний коммит из мастер-ветки.
git reset HEAD~ --hard
git checkout some-new-branch-name
# Теперь ваш коммит полностью независим :)

Команды не сработают, если вы уже закоммитили в публичную ветку. В таком случае может помочь git reset HEAD@{какое-то-количество-коммитов-назад} вместо HEAD~.

Ну отлично. Я закоммитил не в ту ветку!

# Отменяет последний коммит, но оставляет изменения доступными.
git reset HEAD~ --soft
git stash
# Переключаемся на нужную ветку.
git checkout name-of-the-correct-branch
git stash pop
# Добавьте конкретные файл или не парьтесь и закиньте все сразу.
git add .
git commit -m «Тут будет ваше сообщение»
# Теперь ваши изменения в нужной ветке.

Многие в такой ситуации предлагают использовать cherry-pick, так что можете выбрать, что вам больше по душе.

git checkout name-of-the-correct-branch
# Берём последний коммит из мастера.
git cherry-pick master
# Удаляем его из мастера.
git checkout master
git reset HEAD~ --hard

Я пытаюсь запустить diff, но ничего не происходит

Если вы знаете, что изменения были внесены, но diff пуст, то возможно вы индексировали изменения (через add). Поэтому вам нужно использовать специальный флаг.

git diff --staged

Конечно, «это не баг, а фича», но с первого взгляда это чертовски неоднозначно.

Мне нужно каким-то образом отменить коммит, который был сделан 5 коммитов назад

# Найдите коммит, который нужно отменить.
git log
# Можно использовать стрелочки, чтобы прокручивать список вверх и вниз.
# Сохраните хэш нужного коммита.
git revert  [тот хэш]
# Git создаст новый коммит, отменяющий выбранный.
# Отредактируйте сообщение коммита или просто сохраните его.

Вам не обязательно откатываться назад и копипастить старые файлы, замещая ими новые. Если вы закоммитили баг, то коммит можно отменить с помощью revert.

Помимо этого, откатить можно не целый коммит, а отдельный файл. Но следуя канону Git’а, это будут уже совсем другие команды…

Мне нужно отменить изменения в файле

# Найдите хэш коммита, до которого нужно откатиться.
git log
# Сохраните хэш нужного коммита.
git checkout [тот хэш] --path/to/file
# Теперь в индексе окажется старая версия файла.
git commit -m «О май гадбл, вы даже не использовали копипаст»

Именно поэтому checkout — лучший инструмент для отката изменений в файлах.

Давай по новой, Миша, всё х**ня

cd ..
sudo rm -r fucking-git-repo-dir
git clone https://some.github.url/fucking-git-repo-dir.git
cd fucking-git-repo-dir

Если вам нужно полностью откатиться до исходной версии (т. е. отменить все изменения), то можете попробовать сделать так.

Будьте осторожны, эти команды разрушительны и необратимы.

# Получить последнее состояние origin.
git fetch origin
git checkout master
git reset --hard origin/master
# Удалить неиндексированные файлы и папки.
git clean -d --force
# Повторить checkout/reset/clean для каждой испорченной ветки.

***

Эти команды Git нужны для экстренных ситуаций, но пригодиться могут не только они. Про другие команды с пояснениями писали тут: