Como faço para corrigir uma subárvore git depois que a força do projeto upstream foi transferida para o mestre?

13

Venho experimentando o uso da subárvore git e deparamos com a seguinte situação.

Usei a subárvore git para adicionar um projeto externo ao meu repositório, mantive intencionalmente todo o histórico do projeto upstream, pois quero poder me referir ao histórico do projeto e também contribuir com o projeto upstream posteriormente.

Acontece que outro colaborador do projeto upstream empurrou acidentalmente um arquivo grande para a ramificação principal. Para corrigir isso, o projeto upstream reescreveu o histórico e a força empurrada para o mestre. Ao criar meu "monorepo", incluí esse commit e também gostaria de removê-lo.

Como posso atualizar meu repositório para refletir o novo histórico da subárvore?

Minha primeira tentativa foi usar o branch-filter para remover completamente a subárvore e todo o histórico.

git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch upstream-project-dir' --prune-empty HEAD

Depois que a versão antiga da subárvore foi removida, pude adicionar novamente a subárvore usando o novo mestre upstream. No entanto, isso não funcionou porque, por algum motivo, o histórico de confirmação ainda aparece na saída do log do git.

Atualizar

Eu escrevi as etapas para criar um exemplo minimamente reproduzível.

  1. Primeiro, crie um repositório git vazio.

    git init test-monorepo
    cd ./test-monorepo
    
  2. Crie uma confirmação inicial.

    echo hello world > README
    git add README
    git commit -m 'initial commit'
    
  3. Agora adicione uma subárvore para um projeto externo.

    git remote add thirdparty [email protected]:teivah/algodeck.git
    git fetch thirdparty
    git subtree add --prefix algodeck thirdparty master
    
  4. Faça alguns commits no monorepo

    echo dont panic >> algodeck/README.md
    git commit -a -m 'test commit'
    
  5. Agora tente usar o git filter-branch para remover a subárvore.

    git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch algodeck' --prune-empty HEAD
    
  6. Examine a saída do log git, espero ver apenas meu commit inicial.

    git log
    
csnate
fonte
Você tentou git gc --prune = now para descartar os antigos commit? Existem algumas referências à versão antiga confirmada?
Damiano
11
Ainda não tentei isso, mas não git gc --prune=nowexcluiria apenas confirmações que não aparecem git log?
csnate
usar git branch -all (que eu suponho que você esteja usando para ver as confirmações "antigas") deve mostrar também as confirmações não relacionadas à sua ramificação atual.
Damiano
11
Na verdade, eu estava apenas fazendo git log, sem argumentos e ainda vejo os velhos compromissos.
csnate
Por favor, você pode postar seu log do git --pretty --all --graph? Só para entender sua situação
Damiano

Respostas:

0

você já tem o mau commit em sua história e precisa se livrar dele antes de continuar

Vamos supor que você tenha masterdesviado o último commit e não tenha sido capaz de fazer mais nada (eu realmente não tenho suas filiais à vista, então preciso assumir algo para começar)

você pode fazer o checkout do commit anterior e empurrar seu marcador de ramificação 1 passo para trás (ou X passos para trás), o que seria inofensivo em qualquer caso e, em seguida, puxe novamente

por exemplo

git checkout master~1
git branch master -f
git checkout master
git pull
  1. git checkout master~1 para finalizar o commit do pai do mestre, o git adverte que estamos fora dos ramos
  2. git branch master -f para forçar o checkout atual a se tornar mestre novamente, ou seja, na verdade, ele rebobina a ramificação principal para seu commit anterior (ou X commit anterior) e, a partir daqui, não importa se o upstream fez uma força ou não, podemos retomar normalmente, ou mesmo volte à etapa acima, se necessário, só podemos puxar o master novamente, sem perder nada do upstream (o que para nós também pode ser somente leitura, não pressionaremos nada por isso)
  3. git checkout master para estar no nosso ramo principal "rebobinado", o mesmo commit em que estamos entrando, mas agora estar no ramo
  4. git pullpara puxar o mestre novamente (pode ser com ou sem --prune), se desviado a montante, voltaremos à pista daqui, se não, obteremos o mesmo que tínhamos, se obtivemos o mesmo e não era suposto, talvez precisa voltar ao primeiro passo acima e retroceder mais confirmações, por exemplo, git checkout master~5ou o que for (conforme necessário)
arhak
fonte
Eu não acho que isso vai funcionar comgit subtree
csnate
@csnate é possível fazer o checkout de confirmações anteriores de um subrepo e seguir um procedimento muito semelhante. Se você criar um MCVE, seria mais fácil informar os comandos exatos para seguir stackoverflow.com/help/minimal-reproducible-example
arhak
Vou tentar criar um repositório de amostra no GitHub.
csnate
Criei um conjunto de etapas na pergunta original que mostra o problema.
csnate
0
  1. no seu repositório, limpe o histórico de confirmações para este controle remoto:

    git fetch upstream
    
  2. se uma das suas próprias confirmações tiver uma confirmação que inclua o arquivo grande, reescreva seu histórico para que esse arquivo grande não seja mais referenciado

    # using one or more of the following commands :
    git rebase --interactive
    git filter-branch
    ...
    

Com essas duas etapas, o arquivo grande não será mais referenciado por nenhum commit no seu repositório.
Além disso, ele será excluído do disco rígido em algum momento, quando o git executar seu coletor de lixo e os atrasos na expiração dos blobs pendentes forem atingidos.


Se você tiver uma necessidade urgente de excluir esse grande arquivo o mais rápido possível do seu disco rígido:

Executar manualmente

git gc --prune=now
LeGEC
fonte