Как исправить коммит не в ту ветку

Если вы ошиблись в 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 нужны для экстренных ситуаций, но пригодиться могут не только они. Про другие команды с пояснениями писали тут:

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

Время на прочтение
4 мин

Количество просмотров 36K

image

Git — удобная, но довольно сложная система. Сложность, прежде всего, в том, что по невнимательности можно допустить ошибку, которую затем сложно или вообще невозможно исправить. Документация Git предоставляет описание множества команд, которые дают возможность исправить ошибку.

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

Черт, я сделал что-то не то. Дайте мне волшебную машину времени!

git reflog
# you will see a list of every thing you've
# done in git, across all branches!
# each one has an index HEAD@{index}
# find the one before you broke everything
git reset HEAD@{index}
# magic time machine

image

Эта команда позволяет восстановить случайно удаленные данные, откатив слияние, после которого случилась неприятность. refLog используется ну очень часто — давайте скажем спасибо тому, что предложил добавить эту команду.

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

# make your change
git add . # or add individual files
git commit --amend --no-edit
# now your last commit contains that change!
# WARNING: never amend public commits

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

NB! Никогда не изменяйте коммиты в публичной ветке. Применяйте команду лишь для коммитов в локальной ветке, иначе у вас будут проблемы.

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

git commit --amend
# follow prompts to change the commit message

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

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

# create a new branch from the current state of master
git branch some-new-branch-name
# remove the last commit from the master branch
git reset HEAD~ --hard
git checkout some-new-branch-name
# your commit lives in this branch now :)

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

Ну вот, я ошибочно закоммитил не в ту ветку

# undo the last commit, but leave the changes available
git reset HEAD~ --soft
git stash
# move to the correct branch
git checkout name-of-the-correct-branch
git stash pop
git add . # or add individual files
git commit -m "your message here";
# now your changes are on the correct branch

Есть еще один способ, который использует большое количество разработчиков — это cherry-pick.

git checkout name-of-the-correct-branch
# grab the last commit to master
git cherry-pick master
# delete it from master
git checkout master
git reset HEAD~ --hard

Мне нужно запустить diff, но ничего не получается

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

git diff –staged

В общем, это не баг, а фича, но она чертовски неочевидная ¯_(ツ)_/¯

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

# find the commit you need to undo
git log
# use the arrow keys to scroll up and down in history
# once you've found your commit, save the hash
git revert [saved hash]
# git will create a new commit that undoes that commit
# follow prompts to edit the commit message
# or just save and commit

К счастью, не нужно отказываться назад на 5 коммитов, занимаясь копипастом старых и новых файлов. Отменить все это можно при помощи revert.

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

Отменить изменения в файле

А вот и они, эти другие команды.

# find a hash for a commit before the file was changed
git log
# use the arrow keys to scroll up and down in history
# once you've found your commit, save the hash
git checkout [saved hash] -- path/to/file
# the old version of the file will be in your index
git commit -m "Wow, you don't have to copy-paste to undo"

Когда я впервые нашел эту возможность, это было КРУТО, КРУТО, К-Р-У-Т-О. Но если задуматься — почему именно checkout — лучший вариант для отмены изменений в файле? :shakes-fist-at-linus-torvalds:

Все, я сдаюсь

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

Спасибо Eric V. За этот способ. И все жалобы по поводу использования sudo адресуйте ему.

Если вам нужно обнулить изменения и полностью откатиться до исходной версии, то можно попробовать сделать именно так. Но помните — эти команды разрушительны и необратимы.

# get the lastest state of origin
git fetch origin
git checkout master
git reset --hard origin/master
# delete untracked files and directories
git clean -d --force
# repeat checkout/reset/clean for each borked branch

Внимание! Эта статья не является исчерпывающим руководством. И да, есть и другие способы сделать то же самое, причем еще лучше. Но я пришел именно к этим вариантам методом проб и ошибок. Потом у меня появилась сумасшедшая идея поделиться находками. Берите это или уходите!

image

Комментарий эксперта

Даниил Пилипенко, директор центра подбора IT-специалистов SymbioWay и евангелист бэкенд-направления онлайн-университета Skillbox, дополнил перевод мнением о Git и его актуальности для разработчиков.

Git появился в 2005-ом году, и он далеко не сразу занял рынок. Помню, когда мы ещё в 2008-ом году в команде разработчиков внедряли SVN. И даже в 2012-ом одна близкая ко мне компания усиленно внедряла Mercurial. С годами для многих стало очевидным, что Git — это лучшая система контроля версий, и её теперь используют практически все разработчики.

Если вы начинающий разработчик и собираетесь устраиваться на работу, обязательно изучите Git! Вы должны знать, что такое система контроля версий и зачем она нужна, что такое коммит, ветка, как клонировать репозиторий и отправлять сделанные изменения на сервер, как получать новые изменения с сервера, как делать merge, какие бывают виды “reset”. Поначалу эта тема вам может показаться непонятной и сложной, но вам нужно лишь привыкнуть пользоваться Git, и отвыкнуть вы уже не сможете.

In the common case where you forgot to switch from master to your feature branch before commiting:

git checkout -B feature
git branch -f master origin/master

Replace origin/master with the commit you want your master branch to point to. For example, use HEAD~3 if you want it to point 3 commits behind HEAD, or a1b2c3d if you want it to point to the commit with that hash.

The idea is to recreate the feature branch on the current commit and switch to it. Then make the master branch point to the same commit as origin/master.

General case

In the general case where you want to replay the commits done on master on your feature branch, like the following diagram:

A---B---C---D  $old_master                   A---B---C---D  master
    |                                           |        
    |         G---H---I  master <- HEAD  =>      |         G---H---I
    |                                            | 
    `-E---F  feature                             `-E---F---G'--H'--I' feature <- HEAD

Then cherry-pick the commits you made on master on your feature branch using the following commands. Replace $old_master by the hash of the commit master was pointing to before you made your changes.

git checkout feature
git cherry-pick $old_master..master
git branch -f master $old_master

If needed, stash your local changes using git stash --include-untracked, and then unstash them later using git stash pop.

Without rewriting history

Instead of resetting the master branch into the past, you need to git revert the changes, in addition to cherry-picking the changes on the feature branch.

git checkout feature
git cherry-pick $old_master..master
git checkout master
git revert $old_master..
git checkout feature

Make sure not to git merge your commits into feature. Those changes will be ignored if you try to merge your feature branch back into master, because we just reverted them.

Here’s what the result would look like, with rG, rH and rI the revert commits of G, H and I respectively:

A---B---C---D             rG--rH--rI  master
    |                   /
    |         G---H---I-`
    | 
    `-E---F---G'--H'--I' feature <- HEAD


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters

Show hidden characters

# откатываем последний коммит, но не удаляем изменения
git reset HEAD~ –soft
git add .
git stash
# переключаемся на нужную ветку
git checkout имя-верной-ветки
git stash pop
git add .
git commit -m “тут ваш комментарий”
# теперь изменения в нужной ветке

вот мои ветки

aleksey@aleksey:~/Downloads/NTZ/FittingRoom$ git branch -a
develop
feature/addfriendsblock
* feature/getcontacts
remotes/origin/develop
remotes/origin/master

Я создал ветку feature/getcontacts и работал в ней, потом сделал коммит (работа была не закончена)… Потом я забыл переключиться на feature/addfriendsblock и продолжил работать в текущей ветке…

Теперь мне нужно переключиться в ветку feature/addfriendsblock и сделать в ней коммит последних дополнений, так как они не относятся к текущей ветке feature/getcontacts, но я получаю такое сообщение

error: Your local changes to the following files would be overwritten by checkout:
Application/src/main/AndroidManifest.xml
Please, commit your changes or stash them before you can switch branches.

Но мне не нужно коммить последние изменения в текущую ветку они мне нужны в feature/addfriendsblock , как это сделать?

задан 25 июл 2016 в 9:49

Sirop4ik's user avatar

Sirop4ikSirop4ik

10.7k17 золотых знаков59 серебряных знаков125 бронзовых знаков

Если Вам нужно только закоммитить незакоммиченные данные, но в другую ветку, то можно сделать так. Вначале даем команду git stash. Она получит дифф изменений, сохранит его в специальный “карман” и удалит их с ветки. Теперь можно легко переключиться на другую ветку – git checkout feature/addfriendsblock. В новой ветке делаем git stash apply – теперь с “кармана” будут достаны изменения и применены на новом месте (если что, то в кармане они останутся). Вполне возможно, что будут конфликты – их нужно будет решить. После этого код можно проверить и коммитить.

ответ дан 25 июл 2016 в 9:59

KoVadim's user avatar

KoVadimKoVadim

112k6 золотых знаков91 серебряный знак158 бронзовых знаков

3

git checkout --merge feature/addfriendsblock

Потом надо будет провести слияние изменений в файле Application/src/main/AndroidManifest.xml

ответ дан 25 июл 2016 в 10:44

Pavel Mayorov's user avatar

Pavel MayorovPavel Mayorov

57.6k7 золотых знаков71 серебряный знак144 бронзовых знака

2

Добавить комментарий