Для этого есть 2 шага:
- Создать новый пустой коммит
- Переписать историю, чтобы начать с этого пустого коммита
newroot
Для удобства мы поместим новый пустой коммит во временную ветку .
1. Создайте новый пустой коммит
Есть несколько способов сделать это.
Используя только сантехнику
Самый чистый подход - использовать сантехнику Git, чтобы просто создать коммит напрямую, что позволяет избежать прикосновения к рабочей копии, индексу или какой ветке извлечено и т. Д.
Создайте объект дерева для пустой директории:
tree=`git hash-object -wt tree --stdin < /dev/null`
Оберните коммит вокруг него:
commit=`git commit-tree -m 'root commit' $tree`
Создайте ссылку на него:
git branch newroot $commit
Конечно, вы можете перестроить всю процедуру в одну строку, если вы хорошо знаете свою оболочку.
Без сантехники
С помощью обычных фарфоровых команд вы не можете создать пустой коммит без проверки newroot
ветки и повторного обновления индекса и рабочей копии без уважительной причины. Но некоторые могут найти это легче понять:
git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'
Обратите внимание, что на очень старых версиях Git, в которых нет --orphan
переключателя checkout
, вы должны заменить первую строку следующим образом:
git symbolic-ref HEAD refs/heads/newroot
2. Переписать историю, чтобы начать с этого пустого коммита
У вас есть два варианта: перебазирование или переписывание чистой истории.
перебазировка
git rebase --onto newroot --root master
Это имеет преимущество простоты. Однако он также будет обновлять имя и дату коммиттера при каждом последнем коммите в ветви.
Кроме того, с некоторыми крайними историями событий он может даже потерпеть неудачу из-за конфликтов слияния - несмотря на тот факт, что вы переходите на коммит, который ничего не содержит.
Переписать историю
Более чистый подход - переписать ветку. В отличие от с git rebase
, вам нужно посмотреть, с какого коммита начинается ваша ветка:
git replace <currentroot> --graft newroot
git filter-branch master
Переписывание происходит на втором этапе, очевидно; это первый шаг, который требует объяснения. Это git replace
говорит Git, что всякий раз, когда он видит ссылку на объект, который вы хотите заменить, Git должен вместо этого посмотреть на замену этого объекта.
С --graft
выключателем вы говорите что-то немного другое, чем обычно. Вы говорите, что у вас еще нет объекта замены, но вы хотите заменить <currentroot>
объект фиксации точной копией самого себя, за исключением того, что родительский коммит (ы) замены должен быть тем, который вы перечислили (т.е. newroot
коммит) ). Затем git replace
идет вперед и создает этот коммит для вас, а затем объявляет этот коммит в качестве замены вашего исходного коммита.
Теперь, если вы сделаете a git log
, вы увидите, что все уже выглядит так, как вы хотите: ветвь начинается с newroot
.
Тем не менее, обратите внимание, что git replace
это на самом деле не изменяет историю - и при этом не распространяется из вашего хранилища. Он просто добавляет локальное перенаправление в ваш репозиторий с одного объекта на другой. Это означает, что никто не видит эффекта этой замены - только вы.
Вот почему filter-branch
шаг необходим. При этом git replace
вы создаете точную копию с откорректированными родительскими коммитами для корневого коммита; git filter-branch
затем повторяет этот процесс для всех следующих коммитов. Вот где история на самом деле переписывается, чтобы вы могли поделиться ею.