Удалить конкретную фиксацию
Я работал с другом в проекте, и он редактировал кучу файлов, которые не должны были редактироваться. Каким-то образом я объединил свою работу в мою, либо когда я ее потянул, либо когда я попытался просто выбрать конкретные файлы, которые я хотел. Я долго искал и играю, пытаясь понять, как удалить коммиты, содержащие изменения в этих файлах, кажется, что они перебрасываются между revert и rebase, и нет простых примеров, и docs полагаю, что знаю больше, чем я.
Итак, вот упрощенная версия вопроса:
Учитывая следующий сценарий, как удалить commit 2?
$ mkdir git_revert_test && cd git_revert_test
$ git init
Initialized empty Git repository in /Users/josh/deleteme/git_revert_test/.git/
$ echo "line 1" > myfile
$ git add -A
$ git commit -m "commit 1"
[master (root-commit) 8230fa3] commit 1
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 myfile
$ echo "line 2" >> myfile
$ git commit -am "commit 2"
[master 342f9bb] commit 2
1 files changed, 1 insertions(+), 0 deletions(-)
$ echo "line 3" >> myfile
$ git commit -am "commit 3"
[master 1bcb872] commit 3
1 files changed, 1 insertions(+), 0 deletions(-)
Ожидаемый результат
$ cat myfile
line 1
line 3
Вот пример того, как я пытался вернуть
$ git revert 342f9bb
Automatic revert failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>'
and commit the result.
Ответы
Ответ 1
Алгоритм, который Git использует при вычислении diff для обратного, требует, чтобы
- возвращаемые строки не изменяются никакими последующими коммитами.
- что в истории не должно быть никаких других "смежных" коммитов.
Определение "смежных" основано на количестве строк по умолчанию из контекста diff, которое равно 3. Так что если "myfile" был сконструирован так:
$ cat >myfile <<EOF
line 1
junk
junk
junk
junk
line 2
junk
junk
junk
junk
line 3
EOF
$ git add myfile
$ git commit -m "initial check-in"
1 files changed, 11 insertions(+), 0 deletions(-)
create mode 100644 myfile
$ perl -p -i -e 's/line 2/this is the second line/;' myfile
$ git commit -am "changed line 2 to second line"
[master d6cbb19] changed line 2
1 files changed, 1 insertions(+), 1 deletions(-)
$ perl -p -i -e 's/line 3/this is the third line/;' myfile
$ git commit -am "changed line 3 to third line"
[master dd054fe] changed line 3
1 files changed, 1 insertions(+), 1 deletions(-)
$ git revert d6cbb19
Finished one revert.
[master 2db5c47] Revert "changed line 2"
1 files changed, 1 insertions(+), 1 deletions(-)
Тогда все работает как положено.
Второй ответ был очень интересным. Существует функция, которая еще не была официально выпущена (хотя она доступна в Git v1.7.2-rc2), которая называется Revert Strategy. Вы можете вызвать git так:
git revert --strategy resolve
и он должен лучше понять, что вы имели в виду. Я не знаю, каков список доступных стратегий, и при этом я не знаю определения любой стратегии.
Ответ 2
Есть четыре способа сделать это:
Чистый способ, возвращаясь, но сохраняйте в журнале возврат:
git revert --strategy resolve <commit>
Резким способом, удалите всего только последний коммит:
git reset --soft "HEAD^"
Примечание: избегайте git reset --hard
, так как он также отменит все изменения в файлах с момента последнего коммита. Если --soft
не работает, попробуйте --mixed
или --keep
.
Перебазировать (показать журнал последних 5 коммитов и удалить ненужные строки, или переупорядочить, или разбить несколько коммитов в одном, или сделать что-то еще, что вы хотите, это очень универсальный инструмент):
git rebase -i HEAD~5
И если допущена ошибка:
git rebase --abort
Быстрая перебазировка: удалить только определенный коммит, используя его идентификатор:
git rebase --onto commit-id^ commit-id
Альтернативы: вы также можете попробовать:
git cherry-pick commit-id
Еще одна альтернатива:
git revert --no-commit
В крайнем случае, если вам нужна полная свобода редактирования истории (например, потому что git не позволяет вам редактировать то, что вы хотите), вы можете использовать это очень быстрое приложение с открытым исходным кодом: reposurgeon.
Примечание: конечно, все эти изменения выполняются локально, вам следует git push
впоследствии применить изменения к удаленному. И в случае, если ваше хранилище не хочет удалять фиксацию ("ускоренная пересылка не разрешена", что происходит, когда вы хотите удалить коммит, который вы уже выдавили), вы можете использовать git push -f
для принудительной отправки изменений.
Примечание 2: если вы работаете с веткой и вам нужно принудительно нажать, вам следует избегать git push --force
, поскольку это может перезаписать другие ветки (если вы внесли изменения в них, даже если ваша текущая проверка находится в другой ветки). всегда предпочитайте указывать удаленную ветвь при принудительном нажатии: git push --force origin your_branch
.
Ответ 3
Очень простой способ
git rebase -i HEAD ~ x
(x = количество коммитов)
после выполнения файла блокнота откроет "put" drop, кроме вашего commit
и что это все сделано...
просто запустите панель управления git, и изменения будут перенесены на удаленный. Если фиксация, которую вы сбросили, уже была на пульте дистанционного управления, вам придется принудительно выполнить обновление. Поскольку -force считается вредоносным, используйте git push --force-with-lease
.
Ответ 4
Ваш выбор между
- сохранение ошибки и введение исправления и
- удаление ошибки и изменение истории.
Вы должны выбрать (1), если ошибочные изменения были выбраны кем-то другим, и (2), если ошибка ограничена частной недвинутой ветвью.
Git revert - это автоматизированный инструмент для выполнения (1), он создает новую фиксацию, отменяющую предыдущую фиксацию. Вы увидите ошибку и удаление в истории проекта, но люди, которые выходят из вашего репозитория, не будут сталкиваться с проблемами при их обновлении. Он не работает автоматическим образом в вашем примере, поэтому вам нужно отредактировать "myfile" (чтобы удалить строку 2), выполните git add myfile
и git commit
, чтобы справиться с конфликтом. Затем вы закончите с четырьмя коммитами в своей истории, с фиксацией фиксации фиксации 4.
Если никто не заботится о том, чтобы ваша история изменилась, вы можете переписать его и удалить commit 2 (выбор 2). Легкий способ сделать это - использовать git rebase -i 8230fa3
. Это переведет вас в редактор, и вы можете отказаться от ошибочного фиксации, удалив фиксацию (и сохраняя "выбор" рядом с другими сообщениями фиксации. Прочитайте последствия этого.
Ответ 5
Подход 1
Сначала получите хеш коммита (например: 1406cd61), который вам нужно вернуть. простое исправление будет ниже команды,
$ git revert 1406cd61
если вы зафиксировали больше изменений, связанных с файлами 1406cd61 после фиксации 1406cd61, простая команда выше не будет работать. Затем вы должны выполнить следующие шаги: сбор вишни.
Подход 2
Пожалуйста, следуйте приведенным ниже инструкциям, так как мы используем --force, вам нужно иметь права администратора над git-репо, чтобы сделать это.
Шаг 1: Найдите коммит до коммита, который вы хотите удалить git log
Шаг 2: Оформить заказ, совершив git checkout <commit hash>
Шаг 3: Создайте новую ветку, используя текущую фиксацию проверки git checkout -b <new branch>
Шаг 4: Теперь вам нужно добавить коммит после удаленного коммита git cherry-pick <commit hash>
Шаг 5: Теперь повторите шаг 4 для всех остальных коммитов, которые вы хотите сохранить.
Шаг 6: После того, как все коммиты были добавлены в вашу новую ветку и были зафиксированы. Убедитесь, что все в правильном состоянии и работает как задумано. Дважды проверьте, что все было передано: git status
Шаг 7: Переключитесь на сломанную ветку git checkout <broken branch>
Шаг 8: Теперь выполните полную перезагрузку прерванной ветки до фиксации, предшествующей той, которую вы хотите удалить git reset --hard <commit hash>
Шаг 9: Объедините свою фиксированную ветку с этой веткой git merge <branch name>
Шаг 10: Верните объединенные изменения в исходное положение. ВНИМАНИЕ: Это перезапишет удаленный репозиторий! git push --force origin <branch name>
Вы можете выполнить этот процесс, не создавая новую ветку, заменив Шаг 2 & 3 с шагом 8, затем не выполнять шаг 7 & 9.
Ответ 6
Вы можете удалить нежелательные коммиты с помощью git rebase
.
Скажем, вы включили некоторые коммиты из ветки темы коллеги в вашу ветку темы, но позже решили, что не хотите, чтобы эти коммиты.
git checkout -b tmp-branch my-topic-branch # Use a temporary branch to be safe.
git rebase -i master # Interactively rebase against master branch.
В этот момент ваш текстовый редактор откроет интерактивное представление переустановки. Например
- Удалите коммиты, которые вы не хотите, удалив их строки.
- Сохранить и выйти
Если rebase не удалось, удалите временную ветку и попробуйте другую стратегию. В противном случае следуйте приведенным ниже инструкциям.
git checkout my-topic-branch
git reset --hard tmp-branch # Overwrite your topic branch with the temp branch.
git branch -d tmp-branch # Delete the temporary branch.
Если вы перенаправляете ветку вашего сообщения на удаленный компьютер, вам может потребоваться принудительное нажатие, поскольку история фиксации изменилась. Если другие работают в одной ветки, дайте им голову.
Ответ 7
Из других ответов здесь я был немного смущен тем, как git rebase -i
можно было использовать для удаления фиксации, поэтому я надеюсь, что это ОК, чтобы записать мой тестовый пример здесь (очень похожий на OP).
Вот bash
script, который вы можете вставить для создания тестового репозитория в папке /tmp
:
set -x
rm -rf /tmp/myrepo*
cd /tmp
mkdir myrepo_git
cd myrepo_git
git init
git config user.name me
git config user.email [email protected]
mkdir folder
echo aaaa >> folder/file.txt
git add folder/file.txt
git commit -m "1st git commit"
echo bbbb >> folder/file.txt
git add folder/file.txt
git commit -m "2nd git commit"
echo cccc >> folder/file.txt
git add folder/file.txt
git commit -m "3rd git commit"
echo dddd >> folder/file.txt
git add folder/file.txt
git commit -m "4th git commit"
echo eeee >> folder/file.txt
git add folder/file.txt
git commit -m "5th git commit"
В этот момент мы имеем file.txt
с этим содержимым:
aaaa
bbbb
cccc
dddd
eeee
В этот момент HEAD находится на 5-м фиксации, HEAD ~ 1 будет четвертым - и HEAD ~ 4 будет первым фиксатором (так что HEAD ~ 5 не будет существовать). Скажем, мы хотим удалить третью фиксацию - мы можем отправить эту команду в каталог myrepo_git
:
git rebase -i HEAD~4
(Обратите внимание, что git rebase -i HEAD~5
результат с "фатальным: требуется одна ревизия: недопустимый верхний HEAD-5".) Текстовый редактор (см. снимок экрана в @Dennis 'answer) откроется с этим содержимым:
pick 5978582 2nd git commit
pick 448c212 3rd git commit
pick b50213c 4th git commit
pick a9c8fa1 5th git commit
# Rebase b916e7f..a9c8fa1 onto b916e7f
# ...
Итак, мы получаем все коммиты с (но не включая) наш запрошенный HEAD ~ 4. Удалите строку pick 448c212 3rd git commit
и сохраните файл; вы получите ответ от git rebase
:
error: could not apply b50213c... 4th git commit
When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To check out the original branch and stop rebasing run "git rebase --abort".
Could not apply b50213c... 4th git commit
В этот момент откройте myrepo_git/ folder/file.txt
в текстовом редакторе; вы увидите, что он был изменен:
aaaa
bbbb
<<<<<<< HEAD
=======
cccc
dddd
>>>>>>> b50213c... 4th git commit
В принципе, git
видит, что, когда HEAD получил 2-й фиксатор, было содержимое aaaa
+ bbbb
; и затем у него есть патч с добавленным cccc
+ dddd
, который он не знает, как добавить к существующему контенту.
Итак, здесь git
не может решить для вас - это вы должны принять решение: удалив 3-го коммита, вы либо сохраняете внесенные им изменения (здесь, строка cccc
), либо вы нет. Если вы этого не сделаете, просто удалите лишние строки, в том числе cccc
- in folder/file.txt
, используя текстовый редактор, чтобы он выглядел так:
aaaa
bbbb
dddd
... и затем сохраните folder/file.txt
. Теперь вы можете выполнить следующие команды в каталоге myrepo_git
:
$ nano folder/file.txt # text editor - edit, save
$ git rebase --continue
folder/file.txt: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add
Ah - поэтому, чтобы отметить, что мы решили конфликт, мы должны git add
folder/file.txt
, прежде чем делать git rebase --continue
:
$ git add folder/file.txt
$ git rebase --continue
Здесь снова открывается текстовый редактор, показывающий строку 4th git commit
- здесь у нас есть возможность изменить сообщение фиксации (которое в этом случае может быть осмысленно изменено на 4th (and removed 3rd) commit
или подобное). Скажем, вы не хотите - так что просто выходите из текстового редактора без сохранения; как только вы это сделаете, вы получите:
$ git rebase --continue
[detached HEAD b8275fc] 4th git commit
1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master.
На данный момент у вас есть такая история (которую вы также можете проверить с помощью gitk .
или других инструментов) содержимого folder/file.txt
(с, по-видимому, неизменными отметками времени исходных коммитов):
1st git commit | +aaaa
----------------------------------------------
2nd git commit | aaaa
| +bbbb
----------------------------------------------
4th git commit | aaaa
| bbbb
| +dddd
----------------------------------------------
5th git commit | aaaa
| bbbb
| dddd
| +eeee
И если раньше мы решили сохранить строку cccc
(содержимое 3-го git commit, которое мы удалили), мы бы имели:
1st git commit | +aaaa
----------------------------------------------
2nd git commit | aaaa
| +bbbb
----------------------------------------------
4th git commit | aaaa
| bbbb
| +cccc
| +dddd
----------------------------------------------
5th git commit | aaaa
| bbbb
| cccc
| dddd
| +eeee
Хорошо, это был вид чтения, который я надеялся, что нашел бы, чтобы начать grokking, как git rebase
работает с точки зрения удаления коммитов/ревизий; так что надеюсь, что это тоже поможет другим...
Ответ 8
Итак, похоже, что неудачная фиксация была включена в фиксацию слияния в какой-то момент. Снято ли слияние? Если да, то вы захотите использовать git revert
; вам придется вырезать зубы и работать через конфликты. Если нет, тогда вы могли бы либо переустановить, либо вернуться, но вы можете сделать это до фиксации слияния, затем повторите слияние.
Мы не можем помочь вам в первом случае, действительно. После попытки возврата и выяснения, что автоматическая ошибка не удалась, вы должны изучить конфликты и исправить их соответствующим образом. Это точно такой же процесс, как фиксация конфликтов слияния; вы можете использовать git status
, чтобы увидеть, где конфликты, отредактировать несвязанные файлы, найти конфликтующие куски, выяснить, как их разрешить, добавить конфликтующие файлы и, наконец, совершить. Если вы используете git commit
самостоятельно (no -m <message>
), сообщение, которое появляется в вашем редакторе, должно быть сообщением шаблона, созданным git revert
; вы можете добавить примечание о том, как вы исправили конфликты, а затем сохранить и выйти из фиксации.
Во втором случае, устраняя проблему до слияния, есть два подслучая, в зависимости от того, сделали ли вы больше работы после слияния. Если вы этого не сделали, вы можете просто git reset --hard HEAD^
сбить слияние, сделать возврат, а затем повторить слияние. Но я предполагаю, что у вас есть. Итак, вы закончите делать что-то вроде этого:
- создать временную ветвь непосредственно перед слиянием и проверить ее
- выполните возврат (или используйте
git rebase -i <something before the bad commit> <temporary branch>
для удаления плохого коммита)
- повторить слияние
- отмените свою последующую работу:
git rebase --onto <temporary branch> <old merge commit> <real branch>
- удалить временную ветвь
Ответ 9
Итак, вы сделали какую-то работу и подтолкнули ее, позвольте им называть A и B. Ваш коллега тоже сделал некоторые работы, совершает C и D. Вы объединили своих сотрудников в свои руки (слияние commit E), затем продолжили работу, совершил это тоже (совершил F) и обнаружил, что ваш коллега изменил некоторые вещи, которые он не должен был иметь.
Итак, ваша история фиксации выглядит так:
A -- B -- C -- D -- D' -- E -- F
Вы действительно хотите избавиться от C, D и D '. Поскольку вы говорите, что вы объединили своих коллег, работайте в своих, эти коммиты уже "там", поэтому удаление коммитов с использованием, например, git rebase - нет-нет. Поверь мне, я пробовал.
Теперь я вижу два выхода:
-
если вы еще не нажали E и F своему коллеге или кому-либо еще (обычно вашему "серверу происхождения" ), вы все равно можете удалить те из истории. Это ваша работа, которую вы хотите сохранить. Это можно сделать с помощью
git reset D'
(замените D 'фактическим хешем фиксации, который вы можете получить из git log
В этот момент коммиты E и F исчезнут, и изменения снова не будут отменены в вашей локальной рабочей области. На этом этапе я переместил их в ветку или превратил их в патч и сохранил бы его позже. Теперь верните работу своего коллектива либо автоматически с помощью git revert
, либо вручную. Когда вы это сделаете, повторите свою работу поверх этого. У вас могут быть конфликты слияния, но по крайней мере они будут в коде, который вы написали, вместо кода вашей коллеги.
-
Если вы уже нажали на работу, которую вы сделали после того, как ваш коллега совершил ошибку, вы все равно можете попытаться получить "обратный патч" вручную или с помощью git revert
, но поскольку ваша работа "находится на пути", так что вы, вероятно, получите больше конфликтов слияния и более запутанных. Похоже, что вы оказались в...
Ответ 10
git revert --strategy разрешить
если коммит - это слияние:
использовать мерзавец вернуться --strategy решить -m 1