Comment écraser les commits dans un patch avec git format-patch?


163

J'ai huit commits sur une branche que j'aimerais envoyer par courrier électronique à des personnes qui ne sont pas encore éclairées. Jusqu'à présent, tout ce que je fais me donne soit 8 fichiers de correctifs, soit commence à me donner des fichiers de correctifs pour chaque commit dans l'historique de la branche, depuis le début des temps. J'ai utilisé git rebase --interactive pour écraser les commits, mais maintenant tout ce que j'essaye me donne des millions de correctifs depuis le début des temps. Qu'est-ce que je fais mal?

git format-patch master HEAD # yields zillions of patches, even though there's 
                             # only one commit since master

Je suis curieux de savoir quelle méthode vous allez utiliser parmi les propositions ci-dessous. Faites-nous savoir;)
VonC

4
J'utiliserai git diff comme suggéré par Rob Di Marco. Mais je suis au travail pendant deux semaines, après avoir assisté à la naissance de ma deuxième petite fille hier soir, il faudra donc du temps avant de l'utiliser! :)
skiphoppy

2
J'aimerais voir git format-patch --squash master HEAD
schoetbi

1
Essayez master..HEAD pour spécifier une plage de régime.
Konrad Kleine

Réponses:


186

Je recommanderais de le faire sur une branche jetable comme suit. Si vos commits sont dans la branche "newlines" et que vous êtes déjà revenu à votre branche "master", cela devrait faire l'affaire:

[adam@mbp2600 example (master)]$ git checkout -b tmpsquash
Switched to a new branch "tmpsquash"

[adam@mbp2600 example (tmpsquash)]$ git merge --squash newlines
Updating 4d2de39..b6768b2
Fast forward
Squash commit -- not updating HEAD
 test.txt |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

[adam@mbp2600 example (tmpsquash)]$ git commit -a -m "My squashed commits"
[tmpsquash]: created 75b0a89: "My squashed commits"
 1 files changed, 2 insertions(+), 0 deletions(-)

[adam@mbp2600 example (tmpsquash)]$ git format-patch master
0001-My-squashed-commits.patch

J'espère que cela t'aides!


2
C'est ce que j'utilise lorsque je souhaite conserver l'historique localement (au cas où je devrais modifier le patch). Sinon, j'utilise juste rebase -i et écrase les commits.
sebnow

5
pour moi, cela fonctionnait plus fiable avec git comit -m "My squashed commits"sinon cela ajouterait d'autres fichiers non suivis
Sebastian

De plus, vous pouvez passer à un commit spécifique au lieu du master et faire ceci pour créer un patch de commit à commit:git checkout 775ec2a && git checkout -b patch && git merge --squash branchname
Berry M.

131

Juste pour ajouter une autre solution au pot: Si vous utilisez ceci à la place:

git format-patch master --stdout > my_new_patch.diff

Ensuite, il y aura toujours 8 patchs ... mais ils seront tous dans un seul fichier de patch et s'appliqueront comme un seul avec:

git am < my_new_patch.diff

13
J'aime cette solution. Il convient de noter que cela peut parfois créer un patch beaucoup plus gros que la méthode décrite par @Adam Alexander. En effet, certains fichiers peuvent être modifiés plusieurs fois via des validations. Cette méthode traite chaque commit séparément, même si certains fichiers ont été restaurés. Mais la plupart du temps, ce n'est pas un problème.
gumik

1
Cette option est idéale si vous essayez d'éviter de fusionner! (par exemple, vous voulez sauter certains commits). Vous pouvez toujours git rebase -i ...les réduire à un seul.
Jorge Orpinel

21

J'utilise toujours git diff donc dans votre exemple, quelque chose comme

git diff master > patch.txt

15
Sauf que vous perdez tous les messages et métadonnées de validation. La beauté de git format-patch est qu'il est possible de reconstruire une branche entière à partir d'un ensemble de correctifs.
mcepl

la méthode create-branch-and-squash a l'avantage de combiner tous les messages de validation en un seul, mais cette méthode est beaucoup plus rapide si vous souhaitez écrire votre propre message de validation
woojoo666

en fait, un message combiné peut être fait en utilisant git log, voir ma réponse pour un exemple
woojoo666

2
Belle option, mais considérez que 'git diff' ne peut pas inclure de différences binaires (peut-être y a-t-il une option pour le faire).
Santiago Villafuerte

18

Ceci est une adaptation de la réponse d'Adam Alexander, au cas où vos changements seraient dans la branche principale. Procédez comme suit:

  • Crée une nouvelle branche jetable "tmpsquash" à partir du point que nous voulons (recherchez la clé SHA exécutant "git --log" ou avec gitg. Sélectionnez le commit que vous voulez être tmpsquash head, les commits qui sont après cela dans master seront les commits écrasés).
  • Fusionne les changements de master à tmpsquash.
  • Valide les modifications écrasées de tmpsquash.
  • Crée le patch avec les commits écrasés.
  • Retourne à la branche principale

laura@rune:~/example (master)$ git branch tmpsquash ba3c498878054e25afc5e22e207d62eb40ff1f38
laura@rune:~/example (master)$ git checkout tmpsquash
Switched to branch 'tmpsquash'
laura@rune:~/example (tmpsquash)$ git merge --squash master
Updating ba3c498..40386b8
Fast-forward
Squash commit -- not updating HEAD

[snip, changed files]

11 files changed, 212 insertions(+), 59 deletions(-)
laura@rune:~/example  (tmpsquash)$ git commit -a -m "My squashed commits"
[test2 6127e5c] My squashed commits
11 files changed, 212 insertions(+), 59 deletions(-)
laura@rune:~/example  (tmpsquash)$ git format-patch master
0001-My-squashed-commits.patch
laura@rune:~/example  (tmpsquash)$ git checkout master
Switched to branch 'master'
laura@rune:~/example  (master)$

Faire des changements dans master est un anti-pattern si vous ne les poussez pas vous-même à distance. Et si vous voulez, vous n'avez probablement pas besoin de générer des fichiers de correctifs pour eux.
ivan_pozdeev

18

Comme vous le savez déjà, a git format-patch -8 HEADvous donnera huit patchs.

Si vous voulez que vos 8 commits apparaissent comme un seul, et que cela ne vous dérange pas de réécrire l'historique de votre branche ( o-o-X-A-B-C-D-E-F-G-H), vous pouvez:

git rebase -i
// squash A, B, C, D, E ,F, G into H

ou, et ce serait une meilleure solution, rejouez tous vos 8 commits depuis X(le commit avant vos 8 commits) sur une nouvelle branche

git branch delivery X
git checkout delivery
git merge --squash master
git format-patch HEAD

De cette façon, vous n'avez qu'un seul commit sur la branche "livraison", et cela représente tous vos 8 derniers commits


git merge masterfait simplement une avance rapide et n'écrase pas les engagements en un seul. Remplacez par git merge --squash masterpour que tous les commits soient écrasés et visibles dans la zone intermédiaire. Votre flux fonctionnera avec ce changement. (J'utilise la version 2.1.0 de git.)
Stunner

5

Patch de format entre deux balises:

git checkout <source-tag>
git checkout -b <tmpsquash>
git merge --squash <target-tag>
git commit -a -m "<message>"
git format-patch <source-tag>

5

Le moyen le plus simple est d'utiliser git diffet d'ajouter git logsi vous voulez le message de validation combiné que la méthode squash produirait. Par exemple, pour créer le patch entre commit abcdet 1234:

git diff abcd..1234 > patch.diff
git log abcd..1234 > patchmsg.txt

Puis lors de l'application du patch:

git apply patch.diff
git add -A
git reset patch.diff patchmsg.txt
git commit -F patchmsg.txt

N'oubliez pas l' --binaryargument git difflorsque vous traitez avec des fichiers non texte, par exemple des images ou des vidéos.


0

Basé sur la réponse d'Adam Alexander:

git checkout newlines
## must be rebased to master
git checkout -b temporary
# squash the commits
git rebase -i master
git format-patch master
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.