Combiner les deux premiers commits d'un référentiel Git?

197

Supposons que vous ayez un historique contenant les trois commits A, B et C :

A-B-C

Je voudrais combiner les deux validations A et B en une seule validation AB :

AB-C

j'ai essayé

git rebase -i A

qui ouvre mon éditeur avec le contenu suivant:

pick e97a17b B
pick asd314f C

Je change cela en

squash e97a17b B
pick asd314f C

Ensuite, Git 1.6.0.4 dit:

Cannot 'squash' without a previous commit

Y a-t-il un moyen ou est-ce simplement impossible?

Christian
la source

Réponses:

169

Utiliser à git rebase -i --root partir de la version 1.7.12 de Git .

Dans le fichier de rebase interactif, changez la deuxième ligne de commit B en squash et laissez les autres lignes au choix :

pick f4202da A
squash bea708e B
pick a8c6abc C

Cela combinera les deux validations A et B en une seule validation AB .

Trouvé dans cette réponse .

kostmo
la source
126

Tu as essayé:

git rebase -i A

Il est possible de commencer comme ça si vous continuez editplutôt que squash:

edit e97a17b B
pick asd314f C

puis exécutez

git reset --soft HEAD^
git commit --amend
git rebase --continue

Terminé.

David Lichteblau
la source
4
Si vous faites cela pour corriger discrètement un gistub gistub, vous devrez ajouter -m "initial" au commit. ;-)
Bruno Bronosky
1
git rebase --abortpour recommencer et le faire de la bonne façon (ne pas écraser le premier commit dans l'éditeur)
oma
66

Aétait le commit initial, mais maintenant vous voulez Bêtre le commit initial. Les commits git sont des arbres entiers, pas des différences même s'ils sont normalement décrits et vus en termes de différences qu'ils introduisent.

Cette recette fonctionne même s'il y a plusieurs validations entre A et B, et B et C.

# Go back to the last commit that we want
# to form the initial commit (detach HEAD)
git checkout <sha1_for_B>

# reset the branch pointer to the initial commit,
# but leaving the index and working tree intact.
git reset --soft <sha1_for_A>

# amend the initial tree using the tree from 'B'
git commit --amend

# temporarily tag this new initial commit
# (or you could remember the new commit sha1 manually)
git tag tmp

# go back to the original branch (assume master for this example)
git checkout master

# Replay all the commits after B onto the new initial commit
git rebase --onto tmp <sha1_for_B>

# remove the temporary tag
git tag -d tmp
CB Bailey
la source
1
cela déclenche un rebase interactif massif quand je fais legit rebase --onto tmp <sha1_for_B>
Alex
Étant donné que j'avais un tout nouveau dépôt avec seulement deux commits (que je voulais intégrer en un seul), cela a parfaitement fonctionné pour moi. Merci @CB Bailey
RominRonin
10

Dans le cas d'un rebasage interactif, vous devez le faire avant A pour que la liste soit:

pick A
pick B
pick C

devenir:

pick A
squash B
pick C

Si A est le commit initial, vous devez avoir un commit initial différent avant que A. Git ne pense aux différences, cela fonctionnera sur la différence entre (A et B) et (B et C). Par conséquent, la courge ne fonctionne pas dans votre exemple.

Loki
la source
9

Dans le cas où vous avez des centaines ou des milliers de commits, en utilisant la réponse de kostmo de

git rebase -i --root

peut être peu pratique et lent, simplement en raison du grand nombre de validations que le script de rebase doit traiter deux fois , une fois pour générer la liste de l'éditeur de rebase interactif (où vous sélectionnez l'action à effectuer pour chaque validation), et une fois pour réellement exécuter le réapplication des commits.

Voici une solution alternative qui évitera le coût en temps de génération de la liste de l'éditeur de rebase interactif en n'utilisant pas de rebase interactif en premier lieu. De cette façon, c'est similaire à la solution de Charles Bailey . Vous créez simplement une branche orpheline à partir du deuxième commit, puis rebase toutes les validations descendantes par-dessus:

git checkout --orphan orphan <second-commit-sha>
git commit -m "Enter a commit message for the new root commit"
git rebase --onto orphan <second-commit-sha> master

Documentation

Communauté
la source
0

Commande Git pour l'équipe: git rebase -i HEAD ~ [nombre de validations]

Disons que vous avez ci-dessous l'historique des commits git:


pick 5152061 feat: Ajout de la prise en charge de l'enregistrement de l'image. (A)
pick 39c5a04 Fix: corrections de bugs. (B)
Choisissez le correctif 839c6b3: conflit résolu. (C)

Maintenant, vous voulez écraser A et B en AB, procédez comme suit:


pick 5152061 feat: Ajout de la prise en charge de l'enregistrement de l'image. (A)
s 39c5a04 Correction: correction de bogues. (B)
Choisissez le correctif 839c6b3: conflit résolu. (C)

Remarque: pour le squashing commit, nous pouvons utiliser squash ou s. Le résultat final sera:
choisissez 5152061 feat: Ajout de la prise en charge de l'enregistrement de l'image. (AB)
Choisissez le correctif 839c6b3: conflit résolu. (C)

Sumit
la source
-1

Vous devez effectuer un peu de magie en ligne de commande.

git checkout -b a A
git checkout B <files>
git commit --amend
git checkout master
git rebase a

Cela devrait vous laisser avec une branche qui a AB et C comme commits.

Bombe
la source
Étant donné que les anciens et nouveaux commits initiaux n'ont pas d'ancêtre commun, vous pouvez obtenir des conflits inutiles lorsque git essaie d'appliquer toute l'histoire de master à un, même s'ils ont un arbre en commun. En utilisant l'option --onto pour git rebase, vous pouvez indiquer à git le bon endroit pour commencer à postuler.
CB Bailey