Como remover as entradas de log de confirmação selecionadas de um repositório Git, mantendo as alterações?

241

Gostaria de remover as entradas de log de confirmação selecionadas de uma árvore de confirmação linear, para que as entradas não apareçam no log de confirmação.

Minha árvore de commit se parece com:

R--A--B--C--D--E--HEAD

Gostaria de remover as entradas B e C para que elas não apareçam no log de confirmação, mas as alterações de A para D devem ser preservadas. Talvez introduzindo um único commit, para que B e C se tornem BC e a árvore pareça.

R--A--BC--D--E--HEAD

Ou, idealmente, depois que A vem D diretamente. D 'representando mudanças de A para B, B para C e C para D.

R--A--D'--E--HEAD

Isso é possível? se sim, como?

Este é um projeto relativamente novo e, portanto, não possui filiais a partir de agora; portanto, também não há mesclagens.

xk0der
fonte
@ xk0der: "confirma" é o termo certo aqui. rebasepode remover antigos / criar novos commits. Não sei o que significa "confirmar entradas de log".
JFS
@JFSebastian Não vejo nenhum problema com o "log de confirmação" - Log de todos os commits. E eu queria excluir algumas entradas do log - mantendo as alterações reais (as confirmações).
Xk0der 12/08/2012
@ xk0der: as confirmações do git são endereçáveis ​​ao conteúdo, ou seja, se você alterar algo em uma confirmação, por exemplo, sua mensagem de log; você cria um novo commit. Você pode ler o commit do git sem o git e ver por si mesmo .
JFS
@JFSebastian - Obrigado pelos links - eu sei disso - Mas esse tecnicismo realmente muda o problema que eu estava enfrentando e como o coloco à frente? Eu acho que não. No final: eu queria remover "as mensagens de log de confirmação" - sem remover as "alterações de confirmação" - Releia minha pergunta - especialmente o segundo parágrafo. Para adicionar mais git logmostra o "log de confirmação" git-scm.com/docs/git-log . E eu queria me livrar de duas entradas desse log - não das alterações.
Xk0der 12/08/2012

Respostas:

273

O git-rebase (1) faz exatamente isso.

$ git rebase -i HEAD~5

git awsome-ness [git rebase --interactive] contém um exemplo.

  1. Não use git-rebaseem confirmações públicas (remotas).
  2. Verifique se o diretório de trabalho está limpo ( commitou se stashas alterações atuais).
  3. Execute o comando acima. Ele lança o seu $EDITOR.
  4. Substitua pickantes Ce Dpor squash. Ele irá mesclar C e D em B. Se você quiser excluir um commit, basta excluir sua linha.

Se você estiver perdido, digite:

$ git rebase --abort  
jfs
fonte
Obrigado pela resposta rápida. Então, faço o checkout A e faço uma nova recuperação, algo assim git rebase -i D [A]?
Xk0der
3
Como podemos fazer isso em repositórios remotos?
Eray
6
@ Eray: apenas push -fsuas alterações. Não faça isso se você não estiver trabalhando sozinho.
JFS
2
@ ripper234: Corrigi links para apontar a git-rebasemáquina manual e wayback para o post do blog.
jfs
75
# detach head and move to D commit
git checkout <SHA1-for-D>

# move HEAD to A, but leave the index and working tree as for D
git reset --soft <SHA1-for-A>

# Redo the D commit re-using the commit message, but now on top of A
git commit -C <SHA1-for-D>

# Re-apply everything from the old D onwards onto this new place 
git rebase --onto HEAD <SHA1-for-D> master
CB Bailey
fonte
Isso também funciona e me ajudou a entender o que é uma reinicialização suave. Concedido, a resposta "top" também é correta e mais curta, mas obrigado por essa resposta também.
CGP
41

Aqui está uma maneira de remover um ID de confirmação específico, sabendo apenas o ID de confirmação que você deseja remover.

git rebase --onto commit-id^ commit-id

Observe que isso realmente remove a alteração introduzida pelo commit.

rado
fonte
7
O HEAD extra neste comando fará com que o rebase termine com um 'HEAD desanexado' que é indesejável. Deve ser omitido.
Frosty
3
Isso reverte as alterações introduzidas no meu commit-id, o OP quer reter as alterações, basta esmagar as confirmações.
CB Bailey
1
-1 porque não faz o que o OP pediu (em vez disso, destrói algo que ele explicitamente queria reter).
Emil Styrke
1
Embora não faça o que o OP pediu, era exatamente o que eu precisava, então marque +1 em uma resposta útil.
Edvins
20

Para expandir a resposta de JF Sebastian:

Você pode usar o git-rebase para fazer facilmente todos os tipos de alterações no seu histórico de consolidação.

Após executar o git rebase --interactive, você obtém o seguinte em seu $ EDITOR:

pick 366eca1 This has a huge file
pick d975b30 delete foo
pick 121802a delete bar
# Rebase 57d0b28..121802a onto 57d0b28
#
# 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

Você pode mover linhas para alterar a ordem das confirmações e excluir linhas para remover essa confirmação. Ou você pode adicionar um comando para combinar (squash) duas confirmações em uma única confirmação (confirmação anterior é a confirmação acima), editar confirmações (o que foi alterado) ou reformular as mensagens de confirmação.

Acho que pick significa apenas que você deseja deixar esse commit em paz.

(O exemplo é daqui )

idbrii
fonte
14

Você pode remover não interativamente B e C no seu exemplo com:

git rebase --onto HEAD~5 HEAD~3 HEAD

ou simbolicamente,

git rebase --onto A C HEAD

Observe que as alterações em B e C não estarão em D; eles se foram .

Cabeça
fonte
Veja aqui para obter mais informações: sethrobertson.github.io/GitFixUm/fixup.html#remove_deep
Max
3

Mais uma maneira,

git rebase -i ad0389efc1a79b1f9c4dd6061dca6edc1d5bb78a (C's hash)
and
git push origin master  -f

escolha o hash que deseja usá-lo como base e o comando acima deve torná-lo interativo para que você possa compactar todas as principais mensagens (você precisa deixar a mais antiga)

resultados
fonte
2

Acho esse processo muito mais seguro e fácil de entender, criando outro ramo a partir do SHA1 de A e escolhendo as alterações desejadas, para garantir que estou satisfeito com a aparência desse novo ramo. Depois disso, é fácil remover o ramo antigo e renomear o novo.

git checkout <SHA1 of A>
git log #verify looks good
git checkout -b rework
git cherry-pick <SHA1 of D>
....
git log #verify looks good
git branch -D <oldbranch>
git branch -m rework <oldbranch>
Eric Woodruff
fonte
se você fizer isso, você também perderá o comprometimento E, não é? Pelo que entendi, você exclui o mestre e renomeia o retrabalho como mestre (considerando que o fluxo ABCDE é o ramo mestre).
Renan Bandeira
1

Acabei de coletar todas as respostas das pessoas: (sou novo no git plz, use-o apenas para referência)

git rebase para excluir quaisquer confirmações

log git

-first check from which commit you want to rebase

git rebase -i CABEÇA ~ 1

-Here i want to rebase on the second last commit- commit count starts from '1')
-this will open the command line editor (called vim editor i guess)

Então a tela ficará assim:

pick 0c2236d Adicionada nova linha.

Rebase 2a1cd65..0c2236d em 2a1cd65 (1 comando)

#

Comandos:

p, escolha = use confirmação

r, reword = use commit, mas edite a mensagem de commit

e, edit = use commit, mas pare para alterar

s, squash = use commit, mas mescla no commit anterior

f, fixup = like "squash", mas descarte a mensagem de log desta confirmação

x, exec = comando de execução (o resto da linha) usando shell

d, drop = remover confirmação

#

Essas linhas podem ser reordenadas; eles são executados de cima para baixo.

#

Se você remover uma linha aqui, esse compromisso será perdido.

#

No entanto, se você remover tudo, a recuperação será cancelada.

#

Observe que as confirmações vazias são comentadas ~ ~

~
~
~
~
~
~
~
~
~

Altere aqui a primeira linha conforme sua necessidade (usando os comandos listados acima, ou seja, 'drop' para remover o commit etc.) Depois de concluída a edição, pressione ': x' para salvar e sair do editor (isto é apenas para o editor vim)

E depois

empurrão

Se está mostrando um problema, você precisa forçar as alterações para o controle remoto (SEU MUITO CRÍTICO: não force o push se estiver trabalhando em equipe)

origem git push -f

Shrinath Kopare
fonte
-1

Você pode usar git cherry-pick para isso. 'cherry-pick' aplicará uma confirmação no ramo em que você está agora.

então faça

git rebase --hard <SHA1 of A>

depois aplique as confirmações D e E.

git cherry-pick <SHA1 of D>
git cherry-pick <SHA1 of E>

Isso ignorará o commit B e C. Dito isto, pode ser impossível aplicar o commit D ao ramo sem B, então YMMV.

Rory
fonte
2
O OP quer combinar confirmações B, C, D, para não excluir suas alterações.
jfs
3
Eu acho que você quis dizer reset --hard, não rebase --hard(o que não existe) #
Mauricio Scheffer