Git - Удалить между фиксацией
Наша команда делает несколько проектов на PHP. Мы ошибочно передали папку одного проекта другому. Теперь мы хотим удалить этот конкретный коммит из Проекта. Если мы удалим конкретную папку /commit из проекта, то для нашего проекта проблем не будет.
Если мы просто удалим папку и выпустим новый коммит в текущей позиции, то папка будет удалена, но она останется в истории Git. Итак, мы хотим полностью удалить его из ссылок, истории и других вещей из Git.
Мы также можем создать отдельную ветку, но ссылки на авторов будут потеряны. Мы хотим удалить только этот конкретный коммит. У нас нет проблем в переписывании истории или ее повторении, но мы не знаем, как это сделать.
В проекте мы сделали 136 коммитов и хотим удалить коммит № 76. Требуемая информация о ССЗ указана ниже
5d39775b //136th commit
a4df5ee9 //135th commit
6971cf35 //134th commit
.....
....
162f833c //76th commit
32603274 //75th commit
.....
....
9770059 //1st commit
7 ответов
Я перепробовал все предоставленные методы, то есть rebase и cherry-pick. Я здесь для того, чтобы предоставить полную информацию как о проверенных методах, так и о том, что я сделал, чтобы помочь понять вещи лучше
Препараты
Чтобы проверить, что лучше, я сделал эти вещи:
Разветвите удаленный репозиторий, чтобы оригинал не пострадал, и все было легко понять. Поэтому не делайте никаких оригинальных репо, пока не убедитесь, что все сделано правильно или нет.
Сначала взяли чистую копию Git Repo. Как правило, всякий раз, когда мы храним или делаем другие вещи, которые также хранятся в Git как локальная база данных Git. Итак, эта база данных Git не имеет локальных или временных вещей.
Я подсчитал общее количество байтов, занятых папкой.git. Поскольку репо чистое, минимальные байты будут правильными здесь. Чтобы углубиться, я записал как взятые байты, так и укусы, взятые на диск. Как обе вещи разные, так и они:
байт - 7 963 769, а размер на диске - 8 028 160
Если вы используете Windows и установили какой-либо антивирус, вам нужно отключить режим Active/Real-time. Git нуждается в быстром доступе к проверке файлов ввода / вывода. Что происходит, когда файл создается Git, в активном режиме блокируется новый созданный для проверки вирус, а когда Git пытается повторно получить доступ к файлу, который не удается выполнить из-за блокировки антивирусом. В случае неудачи вы должны начать процесс заново с 1.
Метод I - Rebase
В базисном методе это можно сделать двумя способами. Один через --onto, а другой через -i.
-и метод
Я использовал следующую команду:
git rebase -i 162f833c^
и он открыл редактор vim, и я вижу список коммитов, начиная с 162f833c, а не от master. Если конец, следующие строки предоставляются.
# Rebase 3260327..5d39775 onto 3260327
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
Я удалил строку, так что фиксация будет потеряна, сохранил файл и вышел из редактора, а когда я вышел, он начал пере-базировать, как показано:
Rebasing ( 1/64) ..(2/64)
Successfully rebased and updated refs/heads/master.
И затем попытался проверить журнал, чтобы увидеть, что коммит потерян или нет, и в журнале я не смог найти коммит, который я хочу удалить. Означает успешно выполненную команду. Я пытался проверить статус как:
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 64 and 65 different commit(s) each, respectively.
Поскольку коммиты удаляются, но на удаленном компьютере, из которого они получены, требуется их передача. Итак, подтолкнул коммит с силой переопределить предыдущий код.
git push -f
После этого я удалил локальное репо и заново извлек репо с указанным размером:
bytes 6,831,765 bytes
size on disk 6,897,664 bytes
--onto метод
Пройдя по многим блогам и Руководству, я нашел здесь 1 блог, который действительно дал мне знать, как его использовать. Итак, команда, которую я использовал:
git rebase --onto 3260327 79504a5~1
и результат:
First, rewinding head to replay your work on top of it...
Applying: 77th Message
Applying: 78th Message
Applying: 79th Message
....
Last commit
И затем попытался проверить журнал, чтобы увидеть, что коммит потерян или нет, и в журнале я не смог найти коммит, который я хочу удалить. Означает успешно выполненную команду. Я пытался проверить статус как:
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 64 and 65 different commit(s) each, respectively.
Итак, с помощью одной команды все сделано, а затем я сделал принудительный толчок, чтобы проверить байты и т. Д., Как и раньше
git push -f
После этого я удалил локальное репо и заново извлек репо с указанным размером:
bytes - 6,831,389
size on disk - 6,893,568
Rebase -i Vs --onto
Таким образом, после выполнения ребазе метод. Я действительно одобряю метод --onto для удаления промежуточного коммита, так как это одна команда, и байты также сохраняются немного выше, чем метод -i. Реальное преимущество в том, что нам не нужно делать дополнительные вещи в методе --onto.
Метод II - вишня
Cherry-pick действительно хороший метод, но он проходит через много команд, и вам нужно соблюдать осторожность при выполнении команд. Во-первых, я должен создать отдельный файл журнала с
git log --all --decorate --oneline --graph > 1
Который показал мне журнал репо, так как нам нужно просматривать его снова и снова. Из этого репозитория определите коммит, который вы хотите удалить, и скопируйте ключ SHA - последний удачный коммит, обозначив тем самым коммит, до которого мы не хотели ничего менять. Итак, следующая команда будет
git checkout 3260327 -b repair
сгенерированный вывод:
Switched to a new branch 'repair'
Итак, до последнего Good commit мы создали новую ветку. Таким образом, вещи до последнего Good commit не меняются. Я снова запускаю следующую команду, чтобы просмотреть все хорошие коммиты:
git log --decorate --oneline --graph > 2
Я удалил мир - все так же, я хочу видеть только коммиты ремонта ветки. Как показало мне, добрые коммиты помечены правильно. Теперь следующую команду следует использовать с осторожностью, поскольку она включает в себя неверный коммит в качестве начальной и конечной точек последнего выполненного коммита и будет выглядеть так:
git cherry-pick 162f833..5d39775
Выход:
[repair 9ed3f18] xxxxxx
x files changed, xxx insertions(+), xx deletions(-)
[repair 7f06d73] xxxxx
xx files changed, xxx insertions(+), xx deletions(-)
.....
...
Что эта команда делает, что она рекомендует все коммиты, оставляя первый коммит, указанный выше, т.е. (162f833) до последнего предоставленного коммита (5d39775). Таким образом, значение sha commits будет изменено соответствующим образом, так как он повторяет фиксацию по очереди. Теперь пришло время просмотреть журнал как:
git log --all --decorate --oneline --graph > 3
вывод как:
* f61a9a5 (HEAD, repair) xxxxxx
* 25be3b9 xxxxx
* 49be029 xxxxx
.......
.......
| * 5d39775 (origin/master, origin/HEAD, master)
| * a4df5ee xxxxx
| * 6971cf3 xxxxxx
| .......
| .......
| * 162f833 xxxx
|/
* 3260327 xxxxx
......
* 9770059 xxxxx
Таким образом, просмотр графика дал нам знать, что он повторно зафиксировал все коммиты, кроме плохого. показывая вам все старые ключи ша с новыми ключами. Если все в порядке, мы должны сделать ветку восстановления как master и удалить ветку master как:
git checkout master
и вывести как:
Switched to branch 'master'
Сначала оформите мастер ветку, чтобы мы могли отменить изменения в мастер ветке. затем
git reset --hard 3260327
и вывести как:
HEAD is now at 3260327 xxxxx
Он отменяет коммиты после хорошего коммита, и теперь мы должны объединить ветку восстановления с master:
git merge repair
и вывести как:
Updating 3260327..40d290d
Fast-forward
Теперь, если вы просмотрите журнал, он не покажет вам плохой коммит, и все будет сделано. Я сделал принудительный толчок, чтобы проверить байты и т. Д., Как и раньше
git push -f
После этого я удалил локальное репо и заново извлек репо с указанным размером:
bytes - 6,831,556
size on disk - 6,897,664
Ранжирование команд
Rebase --onto [Первая]
bytes - 6,831,389
size on disk - 6,893,568
Cherry Pick [второй]
bytes - 6,831,556
size on disk - 6,897,664
Rebase -i [третий]
bytes 6,831,765 bytes
size on disk 6,897,664 bytes
Я хотел бы отдать предпочтение команде git rebase --onto, так как все легко и просто выполняется одной командой.
В вашей основной ветке вы можете интерактивно перебазировать:
git rebase -i 162f833c^
Это переместится сверху на коммит перед коммитом-нарушителем. Теперь просто удалите оскорбительный коммит из списка, сохраните и создайте редактор (по умолчанию на платформах *nix - vi).
Это переместит вашу ветку поверх коммита до проблемного, без него, что, по-видимому, является тем, чего вы пытаетесь достичь.
Вы можете использовать интерактивный ребаз.
Поскольку коммит, который вы хотите удалить, имеет sha1 162f833c, тогда просто выполните git rebase -i 162f833c^
Откроется текстовый редактор со списком коммитов. Просто удалите строку, соответствующую коммиту, который вы хотите удалить, сохранить и закрыть.
В качестве защитной сетки, когда я делаю подобные вещи, мне нравится сначала ставить тег на HEAD, чтобы, если что-то пошло не так, я мог просто проверить этот тег и восстановить свое первоначальное состояние.
Вы можете автоматически удалить коммит и переписать историю (где ${ref_to_delete}
это коммит, который вы хотите удалить, и предполагает master
это ветка, на которой он находится):
git rebase --onto ${ref_to_delete}~ ${ref_to_delete} master
Если вы используете удаленный репозиторий, вам придется push
с -f
:
git push -f
Вы также можете использовать git cherry-pick
за это:
$ g # g is my alias for git log --all --decorate --oneline --graph --color
* 618f8e5 [2013-12-14 15:13] (HEAD, master) me: good 6
* e27d6d7 [2013-12-14 15:13] me: good 5
* 533f6c3 [2013-12-14 15:13] me: good 4
* 877585f [2013-12-14 15:13] me: bad
* 00c06f3 [2013-12-14 15:12] me: good 3
* e9f80a4 [2013-12-14 15:12] me: good 2
* 3122ba7 [2013-12-14 15:12] me: good 1
* 98da603 [2013-12-14 15:12] me: first
$ git checkout 00c06f3 -b repair
Switched to a new branch 'repair'
$ git cherry-pick 877585f..618f8e5
[repair b340e21] good 4
1 file changed, 1 insertion(+)
[repair 1d2e0d0] good 5
1 file changed, 1 insertion(+)
[repair 1ed1d19] good 6
1 file changed, 1 insertion(+)
$ g
* 1ed1d19 [2013-12-14 15:13] (HEAD, repair) me: good 6
* 1d2e0d0 [2013-12-14 15:13] me: good 5
* b340e21 [2013-12-14 15:13] me: good 4
| * 618f8e5 [2013-12-14 15:13] (master) me: good 6
| * e27d6d7 [2013-12-14 15:13] me: good 5
| * 533f6c3 [2013-12-14 15:13] me: good 4
| * 877585f [2013-12-14 15:13] me: bad
|/
* 00c06f3 [2013-12-14 15:12] me: good 3
* e9f80a4 [2013-12-14 15:12] me: good 2
* 3122ba7 [2013-12-14 15:12] me: good 1
* 98da603 [2013-12-14 15:12] me: first
$ git checkout master
Switched to branch 'master'
$ git reset --hard repair
HEAD is now at 1ed1d19 good 6
$ git branch -d repair
Deleted branch repair (was 1ed1d19).
$ g
* 1ed1d19 [2013-12-14 15:13] (HEAD, master) me: good 6
* 1d2e0d0 [2013-12-14 15:13] me: good 5
* b340e21 [2013-12-14 15:13] me: good 4
* 00c06f3 [2013-12-14 15:12] me: good 3
* e9f80a4 [2013-12-14 15:12] me: good 2
* 3122ba7 [2013-12-14 15:12] me: good 1
* 98da603 [2013-12-14 15:12] me: first
$ # Done :)
git rebase
может быть лучше, так что, по крайней мере, я показываю, что в git обычно есть несколько способов. В конце концов, rebase настолько мощен, что "все значимые операции в git могут быть выражены через команду rebase" ( Линус Торвальдс).
git rebase -i 32603274.. Это покажет огромный список коммитов.
выбрать 162f833c .. выбрать...
измените первый коммит как d 162f833c ..
и сохрани это.
Это отбросит 76-й коммит в одиночку
Некоторые репозитории будут защищены администратором от изменения истории, как в моем случае. Для этого мне пришлось использовать git revert
git вернуть 162f833c
Это также намного безопаснее, поскольку у вас все еще есть плохой коммит, если вы хотите его вернуть. Если вы хотите вернуть его обратно, вам просто нужно сделать:
Git Cherry-Pick 162f833c