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

Foi útil?

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):

  1. Volte para a última confirmação que queremos para formar o commit inicial (detach HEAD):

    git checkout <sha1_for_B>
    
  2. Redefinir o ponteiro do ramo para o commit inicial, mas deixando o índice e trabalhando árvore intacta:

    git reset --soft <sha1_for_A>
    
  3. Alterar a árvore inicial usando a árvore de 'B':

    git commit --amend
    
  4. Temporariamente marcar este novo commit inicial (ou você pode se lembrar do novo commit sha1 manualmente):

    git tag tmp
    
  5. Volte para o ramo original (assumir mestre para este exemplo):

    git checkout master
    
  6. Repetir todos os commits após B para o novo commit inicial:

    git rebase --onto tmp <sha1_for_B>
    
  7. 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top