Удалить конкретную фиксацию

Я работал с другом в проекте, и он редактировал кучу файлов, которые не должны были редактироваться. Каким-то образом я объединил свою работу в мою, либо когда я ее потянул, либо когда я попытался просто выбрать конкретные файлы, которые я хотел. Я долго искал и играю, пытаясь понять, как удалить коммиты, содержащие изменения в этих файлах, кажется, что они перебрасываются между 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 для обратного, требует, чтобы

  1. возвращаемые строки не изменяются никакими последующими коммитами.
  2. что в истории не должно быть никаких других "смежных" коммитов.

Определение "смежных" основано на количестве строк по умолчанию из контекста 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.

В этот момент ваш текстовый редактор откроет интерактивное представление переустановки. Например

git-rebase-todo

  • Удалите коммиты, которые вы не хотите, удалив их строки.
  • Сохранить и выйти

Если 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