Eu perguntei antes sobre como compactar os dois primeiros commits em um repositório git.
Embora as soluções sejam bastante interessantes e não sejam tão perturbadoras quanto outras coisas no git, elas ainda são um pouco da proverbial bolsa de mágoa se você precisar repetir o procedimento várias vezes ao longo do desenvolvimento do seu projeto.
Portanto, prefiro sofrer apenas uma vez e depois poder usar para sempre o rebase interativo padrão.
O que eu quero fazer, então, é ter um commit inicial vazio que existe apenas com o objetivo de ser o primeiro. Sem código, sem nada. Apenas ocupando espaço para que possa ser a base para a recuperação.
Minha pergunta então é: ter um repositório existente, como faço para inserir um novo commit vazio antes do primeiro e fazer com que todos os outros avancem?
Respostas:
Existem 2 etapas para conseguir isso:
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:
Envolva um commit em torno dele:
Crie uma referência a ele:
É 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:Observe que nas versões muito antigas do Git sem a
--orphan
opção de mudarcheckout
, você deve substituir a primeira linha por esta: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
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: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, onewroot
commit ) Em seguidagit 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 partirnewroot
.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. Comgit 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.fonte
--onto newroot
opção é redundante; você pode ficar sem ele porque o argumento que você passanewroot
, é o mesmo que o argumento upstream -newroot
.git-checkout
tinha essa opção. Eu atualizei para mencionar essa abordagem primeiro, obrigado pelo ponteiro.git rebase --merge -s recursive -X theirs --onto newroot --root master
para resolver todos os conflitos automaticamente (consulte esta resposta). @AlexanderKuzinMesclagem das respostas de Aristóteles Pagaltzis e Uwe Kleine-König e do comentário de Richard Bronosky.
(apenas para colocar tudo em um só lugar)
fonte
git rebase newroot master
, por causa de erro.Eu gosto da resposta de Aristóteles. Mas descobriu que, para um repositório grande (> 5000 confirmações), o filtro-branch funciona melhor do que o rebase por várias razões 1) é mais rápido 2) não requer intervenção humana quando há um conflito de mesclagem. 3) pode reescrever as tags - preservando-as. Observe que o branch-filter funciona porque não há dúvidas sobre o conteúdo de cada confirmação - é exatamente o mesmo que antes dessa 're-refração'.
Meus passos são:
Observe que as opções '--tag-name-filter cat' significam que as tags serão reescritas para apontar para as confirmações recém-criadas.
fonte
Usei partes da resposta de Aristóteles e Kent com sucesso:
Isso também reescreverá todas as ramificações (não apenas
master
), além das tags.fonte
refs/original/
e exclui cada referência. As referências que ele exclui já devem ser referenciadas por algum outro ramo, para que elas não desapareçam, apenasrefs/original/
são removidas.timedatectl set-time '2017-01-01 00:00:00'
darnewroot
um timestamp antigo.git rebase --root --onto $emptyrootcommit
deve fazer o truque facilmente
fonte
$emptyrootcommit
é uma variável shell que se expande para nada, com certeza?Eu acho que usar
git replace
egit filter-branch
é uma solução melhor do que usar umgit rebase
:A idéia por trás disso é:
git filter-branch
Aqui está um script para os 2 primeiros passos:
Você pode executar esse script sem risco (mesmo que seja uma boa idéia fazer um backup antes de executar uma ação que nunca fez antes;)) e, se o resultado não for o esperado, exclua os arquivos criados na pasta
.git/refs/replace
e tente novamente; )Depois de verificar se o estado do repositório é o que você espera, execute o seguinte comando para atualizar o histórico de todas as ramificações :
Agora, você deve ver 2 históricos, o antigo e o novo (consulte a ajuda
filter-branch
para obter mais informações). Você pode comparar o 2 e verificar novamente se está tudo bem. Se estiver satisfeito, exclua os arquivos desnecessários:Você pode retornar ao seu
master
ramo e excluir o ramo temporário:Agora, tudo deve ser feito;)
fonte
Fiquei empolgado e escrevi uma versão 'idempotente' desse bom script ... ele sempre inserirá o mesmo commit vazio, e se você executá-lo duas vezes, ele não muda seus hashes de commit toda vez. Então, aqui está minha opinião sobre o git-insert-empty-root :
Vale a pena a complexidade extra? talvez não, mas eu vou usar este.
Isso DEVE também permitir executar esta operação em várias cópias clonadas do repositório e terminar com os mesmos resultados, para que eles ainda sejam compatíveis ... testes ... sim, funciona, mas também é necessário excluir e adicionar seu arquivo. controles remotos novamente, por exemplo:
fonte
Para adicionar uma confirmação vazia no início de um repositório, se você esqueceu de criar uma confirmação vazia imediatamente após o "git init":
fonte
Bem, aqui está o que eu vim com:
fonte
Aqui está o meu
bash
script baseado na resposta de Kent com melhorias:master
quando está pronto;git checkout --orphan
funciona apenas com uma ramificação, sem um estado de cabeça desanexada, por isso foi verificado o tempo suficiente para fazer com que a nova raiz seja confirmada e excluída;filter-branch
(Kent deixou um espaço reservado para substituição manual);filter-branch
operação reescreve apenas as ramificações locais, e não os controles remotos tambémfonte
Para alternar a confirmação raiz:
Primeiro, crie o commit que você deseja como o primeiro.
Segundo, mude a ordem dos commits usando:
git rebase -i --root
Um editor aparecerá com as confirmações até a confirmação da raiz, como:
escolha 1234 mensagem raiz antiga
pick 0294 Um commit no meio
escolha 5678 commit que você deseja colocar na raiz
Você pode colocar o commit que deseja primeiro, colocando-o na primeira linha. No exemplo:
escolha 5678 commit que você deseja colocar na raiz
escolha 1234 mensagem raiz antiga
pick 0294 Um commit no meio
Saia do editor, a ordem de confirmação será alterada.
PS: Para alterar o editor que o git usa, execute:
git config --global core.editor name_of_the_editor_program_you_want_to_use
fonte
Combinando o melhor e o mais recente. Sem efeitos colaterais, sem conflitos, mantendo as tags.
Observe que, no GitHub, você perderá os dados de execução do IC e o PR poderá ficar bagunçado, a menos que outras ramificações também sejam corrigidas.
fonte
Resposta a seguir Aristóteles Pagaltzis e outros, mas usando comandos mais simples
Observe que seu repositório não deve conter modificações locais aguardando confirmação.
Nota
git checkout --orphan
funcionará em novas versões do git, eu acho.Observe que na maioria das vezes
git status
fornece dicas úteis.fonte
Inicie um novo repositório.
Defina sua data de volta para a data de início desejada.
Faça tudo da maneira que você gostaria que tivesse feito, ajustando a hora do sistema para refletir quando você desejasse ter feito dessa maneira. Puxe arquivos do repositório existente conforme necessário para evitar muita digitação desnecessária.
Quando você chegar hoje, troque os repositórios e pronto.
Se você é apenas louco (estabelecido), mas razoavelmente inteligente (provavelmente, porque você precisa ter uma certa quantidade de inteligência para pensar em idéias malucas como essa), você guiará o processo.
Isso também será mais agradável quando você decidir que deseja que o passado aconteça de outra maneira daqui a uma semana.
fonte
Eu sei que este post é antigo, mas esta página é a primeira no Google "inserindo commit git".
Por que complicar as coisas simples?
Você tem ABC e deseja ABZC.
git rebase -i trunk
(ou qualquer coisa antes de B)git add ..
git commit
(git commit --amend
que editará B e não criará Z)[Você pode fazer quantas
git commit
quiser aqui para inserir mais confirmações. Obviamente, você pode ter problemas com a etapa 5, mas resolver conflitos de fusão com o git é uma habilidade que você deve ter. Se não, pratique!]git rebase --continue
Simples, não é?
Se você entende
git rebase
, adicionar um commit 'root' não deve ser um problema.Divirta-se com o git!
fonte
git rebase
não pode fazer isso.