Existem 2 etapas para conseguir isso:
- Crie uma nova confirmação vazia
- Reescreva o histórico para iniciar a partir desta confirmação vazia
Colocaremos o novo commit vazio em uma ramificação temporária newroot
por conveniência.
1. Crie uma nova confirmação vazia
Há várias maneiras de fazer isso.
Usando apenas encanamento
A abordagem mais limpa é usar o encanamento do Git para criar um commit diretamente, o que evita tocar na cópia de trabalho ou no índice ou em qual filial está com check-out, etc.
Crie um objeto de árvore para um diretório vazio:
tree=`git hash-object -wt tree --stdin < /dev/null`
Envolva um commit em torno dele:
commit=`git commit-tree -m 'root commit' $tree`
Crie uma referência a ele:
git branch newroot $commit
É claro que você pode reorganizar todo o procedimento em uma linha, se conhecer bem o seu shell.
Sem encanamento
Com comandos regulares de porcelana, você não pode criar uma confirmação vazia sem verificar a newroot
ramificação e atualizar o índice e a cópia de trabalho repetidamente, sem um bom motivo. Mas alguns podem achar isso mais fácil de entender:
git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'
Observe que nas versões muito antigas do Git sem a --orphan
opção de mudar checkout
, você deve substituir a primeira linha por esta:
git symbolic-ref HEAD refs/heads/newroot
2. Reescreva o histórico para começar com este commit vazio
Você tem duas opções aqui: reorganizar ou reescrever um histórico limpo.
Rebasing
git rebase --onto newroot --root master
Isso tem a virtude da simplicidade. No entanto, também atualizará o nome e a data do consolidador em cada último commit na ramificação.
Além disso, com alguns históricos de casos extremos, pode até falhar devido a conflitos de mesclagem - apesar do fato de você estar reestruturando em um commit que não contém nada.
Reescrever o histórico
A abordagem mais limpa é reescrever a ramificação. Ao contrário de git rebase
, você precisará procurar em qual commit seu branch começa:
git replace <currentroot> --graft newroot
git filter-branch master
A reescrita acontece no segundo passo, obviamente; é o primeiro passo que precisa de explicação. O que git replace
faz é dizer ao Git que, sempre que vir uma referência a um objeto que você deseja substituir, o Git deve procurar a substituição desse objeto.
Com a --graft
opção, você está dizendo algo ligeiramente diferente do normal. Você está dizendo que ainda não tem um objeto de substituição, mas deseja substituir o <currentroot>
objeto de confirmação por uma cópia exata de si mesmo, exceto que o (s) commit (s) pai da substituição deve ser aquele que você listou (ou seja, o newroot
commit ) Em seguida git replace
, prossegue e cria esse commit para você e declara esse commit como substituto para o commit original.
Agora, se você fizer um git log
, verá que as coisas já estão como você deseja: o ramo começa a partir newroot
.
No entanto, observe que git replace
na verdade não modifica o histórico - nem se propaga para fora do seu repositório. Ele simplesmente adiciona um redirecionamento local ao seu repositório de um objeto para outro. O que isso significa é que ninguém mais vê o efeito dessa substituição - somente você.
É por isso que o filter-branch
passo é necessário. Com git replace
você, crie uma cópia exata com confirmações pai ajustadas para a confirmação raiz; git filter-branch
em seguida, repete esse processo para todas as confirmações a seguir. É aí que a história é realmente reescrita para que você possa compartilhá-la.