681 votes

Squash les deux premiers s'engage dans Git?

Avec git rebase --interactive <commit> vous pouvez squash n'importe quel nombre de commits ensemble en un seul.

C'est génial, sauf si vous voulez à la courge s'engage dans la première validation. Qui semble impossible à faire.

Existe-il des moyens pour y parvenir?


Modérément lié:

Dans une question connexe, j'ai réussi à venir avec une approche différente de la nécessité de les écraser contre le premier commit, qui est, eh bien, pour faire le deuxième.

Si vous êtes intéressé: git: comment insérer un commit comme la première, fait passer tous les autres?

954voto

VonC Points 414372

Mise à jour en juillet 2012 (git 1.7.12+)

Vous pouvez maintenant rebase tous s'engage jusqu'à la racine, et sélectionnez le deuxième commettre Y d'être écrasé avec le premier X.

git rebase -i --root master

pick sha1 X
squash sha1Y Y
pick sha1 Z

git rebase [-i] --root $tip

peut maintenant être utilisé pour réécrire toute l'histoire menant à "$tip" à la racine de la validation.

Voir commettre df5df20c1308f936ea542c86df1e9c6974168472 de Chris Webb (arachsys).


Origine de la réponse (février 2009)

Je crois que vous trouverez différentes recettes pour que, dans la question "Comment puis-je combiner les deux premières s'engage d'un dépôt git?"

Charles Bailey, à la condition la plus détaillée de la réponse, rappelant qu'un commit est un arbre complet (pas seulement les différences par rapport à un état précédent).
Et voici l'ancien commit (le premier commit") et le nouveau commit (suite de l'écrasement) n'aura pas d'ancêtre commun.
Ce qui signifie que vous ne pouvez pas "commit --amend" la première s'engager dans de nouvelles, et puis rebase sur le nouveau initiale de commettre l'histoire de la précédente initiale commit (beaucoup de conflits)

(Cette dernière phrase n'est plus vrai avec l' git rebase -i --root <aBranch>)

Plutôt (avec A originaux "initial s'engager", et B un commit besoin d'être écrasé dans le premier):

  1. Revenir à la dernière s'engager à ce que nous voulons pour former la première commit (détacher la TÊTE):

    git checkout <sha1_for_B>
    
  2. Réinitialisation de la branche pointeur vers la première s'engager, mais en laissant l'index et de l'arbre de travail intact:

    git reset --soft <sha1_for_A>
    
  3. Modifier l'arbre initial à l'aide de l'arbre à partir de 'B':

    git commit --amend
    
  4. Temporairement balise de cette nouvelle initial s'engager (ou de vous souvenir le nouveau commit sha1 manuellement):

    git tag tmp
    
  5. Revenir à l'origine de la branche (à supposer maître pour cet exemple):

    git checkout master
    
  6. Relire tous les commits après B sur la nouvelle initiale s'engager:

    git rebase --onto tmp <sha1_for_B>
    
  7. Retirez l'étiquette temporaire:

    git tag -d tmp
    

De cette façon, le "rebase --onto" n'introduit pas de conflits au cours de la fusion, car il rebases histoire faite après le dernier commit (B) à être écrasé dans le premier (qui était en A) tmp (représentant l'écrasé de nouveaux initiale commettre): trivial d'avance rapide, de fusions.

Qui fonctionne pour "A-B", mais aussi "A-...-...-...-B" (n'importe quel nombre de validations peuvent être écrasé dans la première de cette façon)

33voto

fonsinchen Points 201

J'ai retravaillé VonC du script à tout faire automatiquement et me demandez pas pour quoi que ce soit. Donnez-vous deux de commettre SHA1s et il va de squash tout ce qui entre dans un commit nommé "écrasé l'histoire":

#!/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

24voto

Ryan Thompson Points 7501

Pour ce que ça vaut, j'évite ce problème en créant toujours un "no-op" premier commit, dans lequel la seule chose dans le référentiel est vide .gitignore:

https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit

De cette façon, il n'y a jamais aucune raison de jouer avec le premier commit.

5voto

Antony Hatchkins Points 5831

Cela permettra de squash deuxième s'engager dans la première:

A-B-C-... -> AB-C-...

git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" = <sha1ofA> ];
    then
        skip_commit "$@";
    else
        git commit-tree "$@";
    fi
' HEAD

Message de Commit pour AB seront prises à partir de B (bien que je préfère partir d'Une).

A le même effet que Uwe Kleine-König réponse, mais travaille pour non-initiale A ainsi.

3voto

hillu Points 4033

L'écrasement de la première et de la deuxième commettre résultat serait le premier à valider être réécrit. Si vous avez plus d'une branche qui est basé sur le premier commit, vous souhaitez couper cette branche.

Considérons l'exemple suivant:

a---b---HEAD
 \
  \
   '---d

L'écrasement de a et b dans un nouveau commit "ab" deux arbres distincts qui, dans la plupart des cas, n'est pas souhaitable puisque git-fusion et git-git rebase n'auront plus de travail entre les deux branches.

ab---HEAD

a---d

Si tu le veux vraiment, il peut être fait. Jetez un oeil à git-filter-branch pour un puissant (et dangereux) de l'outil pour la réécriture de l'histoire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X