Question Squash les deux premiers commits dans Git? [dupliquer]


Cette question a déjà une réponse ici:

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

C'est très bien, sauf si vous voulez écraser les commits dans le commit initial. Cela semble impossible à faire.

Y a-t-il des moyens de l'atteindre?


Modérément lié:

Dans une question connexe, j'ai réussi à trouver une approche différente de la nécessité d'écraser le premier engagement, qui est, bien, d'en faire le second.

Si vous êtes intéressé: git: comment insérer un commit comme premier, déplacer tous les autres?


432
2018-02-28 20:55


origine


Réponses:


Mise à jour de juillet 2012 (git 1.7.12+)

Vous pouvez maintenant rebaser toutes les validations jusqu'à la racine et sélectionner le second commit Y être écrasé avec le premier X.

git rebase -i --root master

pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip

Cette commande peut maintenant être utilisée pour réécrire tout l'historique menant de "$tip"jusqu'à la racine de la validation.

Voir commit df5df20c1308f936ea542c86df1e9c6974168472 sur GitHub de Chris Webb (arachsys).


Réponse d'origine (février 2009)

Je crois que vous trouverez différentes recettes pour cela dans la question SO "Comment puis-je combiner les deux premières validations d'un référentiel git?"

Charles Bailey fourni le plus réponse détaillée, nous rappelant qu'un commit est un arbre complet (pas seulement des états précédents).
Et ici l'ancien commit (le "commit initial") et le nouveau commit (résultat de l'écrasement) n'auront pas d'ancêtre commun.
Cela signifie que vous ne pouvez pas "commit --amend"le commit initial dans le nouveau, puis rebase sur la nouvelle initiale commet l'histoire du commit initial précédent (beaucoup de conflits)

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

Plutôt (avec A l'original "commit initial", et B un commit suivant devait être écrasé dans le commit initial):

  1. Revenez à la dernière validation que nous souhaitons pour la validation initiale (detach HEAD):

    git checkout <sha1_for_B>
    
  2. Réinitialiser le pointeur de branche sur la validation initiale, mais en laissant l'index et l'arborescence de travail intacts:

    git reset --soft <sha1_for_A>
    
  3. Modifier l'arbre initial en utilisant l'arbre de 'B':

    git commit --amend
    
  4. Marquer temporairement cette nouvelle validation initiale (ou vous pouvez vous souvenir du nouveau commit sha1 manuellement):

    git tag tmp
    
  5. Revenez à la branche d'origine (supposez maître pour cet exemple):

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

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

    git tag -d tmp
    

De cette façon, le "rebase --onto"n'introduit pas de conflits lors de la fusion, puisqu'elle rebase l'histoire fait après le dernier commit (B) d'être écrasé dans l'initiale (qui était A) à tmp (représentant la nouvelle validation initiale écrasée): fusionnement rapide trivial uniquement.

Cela fonctionne pour "A-B", mais aussi "A-...-...-...-B"(tout nombre de commits peut être écrasé de la manière initiale)


573
2018-02-28 22:18



J'ai retravaillé le script de VonC pour tout faire automatiquement et ne pas me demander quoi que ce soit. Vous lui donnez deux SHA1 de commit et il va écraser tout ce qui les sépare en un commit nommé "historique écrasé":

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

28
2018-04-18 13:05



Pour ce que ça vaut, j'évite ce problème en créant toujours une première validation "no-op", dans laquelle la seule chose dans le dépôt est un .gitignore vide:

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.


21
2017-10-07 20:30



Si vous voulez simplement écraser tous les commits en un seul commit initial, réinitialisez simplement le référentiel et modifiez le premier commit:

git reset hash-of-first-commit
git add -A
git commit --amend

Git reset laissera l'arbre de travail intact, donc tout est toujours là. Il suffit donc d'ajouter les fichiers en utilisant les commandes git add, et de modifier le premier commit avec ces changements. Par rapport à rebase -i, vous perdrez la possibilité de fusionner les commentaires git.


7
2018-05-12 08:57



Cela écrasera deuxième commit dans le premier:

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

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

Le message d'engagement pour AB sera pris de B (bien que je préférerais de A).

A le même effet que la réponse d'Uwe Kleine-König, mais fonctionne également pour les non-initiales.


5
2018-03-16 10:14



L'écrasement de la première et deuxième validation entraînerait la réécriture du premier commit. Si vous avez plusieurs branches basées sur la première validation, vous devez couper cette branche.

Considérez l'exemple suivant:

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

Si vous écrasez a et b dans un nouvel commit "ab", vous obtiendrez deux arbres distincts qui, dans la plupart des cas, ne sont pas souhaitables car git-merge et git-rebase ne travaillera plus entre les deux branches.

ab---HEAD

a---d

Si vous voulez vraiment cela, cela peut être fait. Jettes un coup d'oeil à git-filter-branch pour un outil puissant (et dangereux) pour la réécriture de l'histoire.


3
2018-03-01 14:41



Vous pouvez utiliser git filter-branch pour cela. par exemple.

git filter-branch --parent-filter \
'if test $GIT_COMMIT != <sha1ofB>; then cat; fi'

Cela entraîne AB-C jeter le journal de validation de A.


3
2018-01-17 18:22