Esmagar os dois primeiros commits em Git? [duplicado]
-
11-09-2019 - |
Pergunta
Esta questão já tem uma resposta aqui:
Com git rebase --interactive <commit>
você pode esmagar qualquer número de commits juntos em um único.
Isso é tudo ótimo, a menos que você quiser commits de squash para o commit inicial. Isso parece impossível de fazer.
Existem maneiras de conseguir isso?
Moderadamente relacionados:
Em uma questão relacionada, consegui chegar a uma abordagem diferente para a necessidade de esmagamento contra o primeiro commit, que é, assim, para torná-lo o segundo.
Se você estiver interessado: git: como inserir um commit como o primeiro, mudando todos os outros
Solução
Atualização de julho de 2012 ( git 1.7.12 + )
Agora você pode rebase todos os commits até raiz e selecione o Y
segunda comprometer-se a ser esmagado com o primeiro X
.
git rebase -i --root master
pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip
Este comando agora pode ser usado para reescrever toda a história que conduz da "
$tip
" até a raiz cometer.
cometer df5df20c1308f936ea542c86df1e9c6974168472 no GitHub de Chris Webb (arachsys
) .
resposta Original (fevereiro de 2009)
Eu acredito que você vai encontrar receitas diferentes para que na questão SO " Como posso combinar os dois primeiros commits de um repositório git? "
Charles Bailey desde que haja o máximo detalhou resposta , lembrando-nos que um commit é uma árvore cheia (e não apenas diffs de um estado anterior).
E aqui o velho commit (o "commit inicial") eo novo commit (resultado do esmagamento) não terá nenhum ancestral comum.
Isso significa que você não pode "commit --amend
" o commit inicial para um novo, e depois rebase para o novo commit inicial da história da inicial submissão anterior (lotes de conflitos)
(Essa última frase não é mais verdade com git rebase -i --root <aBranch>
é)
Em vez (com A
o original "inicial comprometer", e uma subsequente B
cometem necessário para ser comprimida em a um inicial):
-
Volte para a última confirmação que queremos para formar o commit inicial (detach HEAD):
git checkout <sha1_for_B>
-
Redefinir o ponteiro do ramo para o commit inicial, mas deixando o índice e trabalhando árvore intacta:
git reset --soft <sha1_for_A>
-
Alterar a árvore inicial usando a árvore de 'B':
git commit --amend
-
Temporariamente marcar este novo commit inicial (ou você pode se lembrar do novo commit sha1 manualmente):
git tag tmp
-
Volte para o ramo original (assumir mestre para este exemplo):
git checkout master
-
Repetir todos os commits após B para o novo commit inicial:
git rebase --onto tmp <sha1_for_B>
-
Remova a tag temporária:
git tag -d tmp
Assim, o "rebase --onto
" não introduz conflitos durante a fusão, uma vez que rebases história feita após o último commit (B
) para ser esmagado na inicial (que era A
) para tmp
(representando o esmagado novo commit inicial): triviais fusões fast-forward somente.
que funciona para "A-B
", mas também "A-...-...-...-B
" (qualquer número de commits pode ser comprimida em um inicial desta forma)
Outras dicas
Eu reformulado roteiro de VonC fazer tudo automaticamente e não me pedir nada. Você dar-lhe dois cometer SHA1s e vai esmagar tudo entre eles em um commit chamado "história esmagado":
#!/bin/sh
# Go back to the last commit that we want
# to form the initial commit (detach HEAD)
git checkout $2
# reset the branch pointer to the initial commit (= $1),
# but leaving the index and working tree intact.
git reset --soft $1
# amend the initial tree using the tree from $2
git commit --amend -m "squashed history"
# remember the new commit sha1
TARGET=`git rev-list HEAD --max-count=1`
# go back to the original branch (assume master for this example)
git checkout master
# Replay all the commits after $2 onto the new initial commit
git rebase --onto $TARGET $2
Por que vale a pena, eu evitar este problema, sempre criando um "não-op" primeira cometer, em que a única coisa no repositório é um .gitignore vazio:
https://github.com/DarwinAwardWinner/ git-custom-comandos / blob / master / bin / git-MyInit
Dessa forma, nunca há qualquer razão para mexer com o primeiro cometer.
Se você simplesmente quer esmagar todos os commits em um único, cometer inicial, apenas redefinir o repositório e alterar o primeiro commit:
git reset hash-of-first-commit
git add -A
git commit --amend
Redefinir Git vai deixar a árvore de trabalho intacta, então tudo ainda está lá. Então, basta adicionar os arquivos usando comandos add git, e alterar o primeiro comprometer com essas mudanças. Comparado com rebase -i você vai perder a capacidade de mesclar os comentários git embora.
Isto irá esmagar segunda cometer na primeira:
A-B-C-... -> AB-C-...
git filter-branch --commit-filter '
if [ "$GIT_COMMIT" = <sha1ofA> ];
then
skip_commit "$@";
else
git commit-tree "$@";
fi
' HEAD
Commit mensagem para AB serão tomadas a partir B (embora eu prefiro a partir de A).
Tem o mesmo efeito que a resposta de Uwe Kleine-König, mas funciona para um não-inicial também.
Squashing o primeiro eo segundo cometer resultaria no primeiro commit sendo reescrito. Se você tiver mais de um ramo que é baseado fora o primeiro cometer, você cortar esse ramo.
Considere o seguinte exemplo:
a---b---HEAD
\
\
'---d
Squashing a e b em um novo commit "ab" resultaria em duas árvores distintas, que na maioria dos casos não é desejável, pois git-merge e git-rebase vontade já não funcionam nos dois ramos.
ab---HEAD
a---d
Se você realmente quer isso, ele pode ser feito. Ter um olhar para git-filter-branch para uma ferramenta poderosa (e perigoso) para a história reescrita.
Você pode usar git filter-branch para isso. por exemplo.
git filter-branch --parent-filter \
'if test $GIT_COMMIT != <sha1ofB>; then cat; fi'
Isso resulta em AB-C jogando fora o log de confirmação de uma.
Você pode usar rebase interativo para modificar os dois últimos commits antes de terem sido empurrados para um controle remoto
git rebase HEAD^^ -i
Há uma maneira mais fácil de fazer isso. Vamos supor que você está no ramo master
Criar um novo ramo órfão que irá remover todos cometem história:
$ git checkout --orphan new_branch
Adicione sua inicial mensagem de confirmação:
$ git commit -a
Se livrar do velho branch master não mesclado:
$ git branch -D master
Renomear o seu actual new_branch
galho em master
:
$ git branch -m master