Combinaison de plusieurs validations en une seule avant de pousser


132

Cette question concerne non seulement comment accomplir cette tâche, mais aussi si cela est une bonne ou une mauvaise pratique avec Git.

Considérez que localement, je travaille principalement sur la branche master, mais j'ai créé une branche topique que j'appellerai "topical_xFeature". En train de travailler sur "topical_xFeature" et de basculer dans les deux sens pour effectuer d'autres travaux sur la branche master, il s'avère que j'ai fait plus d'un commit sur la branche "topical_xFeature", mais entre chaque commit, je n'ai pas fait pousser.

Tout d'abord , considérez-vous cette mauvaise pratique? Ne serait-il pas plus sage de s'en tenir à un commit par branche et par push? Dans quels cas serait-il bon d'avoir plusieurs commits sur une branche avant qu'une poussée ne soit faite?

Deuxièmement , comment dois-je effectuer au mieux pour amener les multiples commits de la branche topical_xFeature dans la branche master pour un push? Est-ce gênant de ne pas s'en inquiéter et de simplement faire le push où plusieurs commits sont poussés, ou est-il moins ennuyeux de fusionner les commits en un seul puis de pousser? Encore une fois, comment faire cela?

Réponses:


140

Pour votre première question, non, il n'y a rien de mal à pousser plusieurs commits à la fois. Plusieurs fois, vous voudrez peut-être diviser votre travail en quelques petits commits logiques, mais ne les augmentez que lorsque vous sentez que toute la série est prête. Ou vous pouvez faire plusieurs commits localement lorsque vous êtes déconnecté, et vous les poussez tous une fois que vous êtes à nouveau connecté. Il n'y a aucune raison de vous limiter à un commit par push.

Je trouve généralement que c'est une bonne idée de conserver à chaque commit un seul changement logique et cohérent, qui inclut tout ce dont il a besoin pour fonctionner (donc, cela ne laisse pas votre code dans un état cassé). Si vous avez deux commits, mais qu'ils provoqueraient la rupture du code si vous n'appliquiez que le premier, il peut être judicieux d'écraser le deuxième commit dans le premier. Mais si vous avez deux commits où chacun apporte un changement raisonnable, les pousser en tant que commits séparés est très bien.

Si vous voulez écraser plusieurs commits ensemble, vous pouvez utiliser git rebase -i. Si vous êtes sur une branche topical_xFeature, vous courriez git rebase -i master. Cela ouvrira une fenêtre d'éditeur, avec un tas de commits listés avec le préfixe pick. Vous pouvez tout changer sauf le premier en squash, ce qui indiquera à Git de conserver toutes ces modifications, mais les écrasera dans le premier commit. Après avoir fait cela, vérifiez masteret fusionnez dans votre branche de fonctionnalités:

git checkout topical_xFeature
git rebase -i master
git checkout master
git merge topical_xFeature

Sinon, si vous voulez juste tout squash topical_xFeaturedans master, vous pouvez simplement faire ce qui suit:

git checkout master
git merge --squash topical_xFeature
git commit

Celui que vous choisissez dépend de vous. En général, je ne craindrais pas d'avoir plusieurs commits plus petits, mais parfois vous ne voulez pas vous soucier de commits mineurs supplémentaires, alors vous les écrasez simplement en un seul.


1
Après avoir fusionné avec --squash, je ne peux pas supprimer la branche de rubrique avec git branch -d topic. Pourquoi git n'est-il pas capable d'identifier que toutes les modifications sont fusionnées?
balki

7
@balki Parce que Git détecte si les correctifs sont fusionnés selon qu'ils apparaissent dans l'historique de la branche donnée. Le squashing les modifie; ils deviennent un nouveau commit, et bien que ce nouveau commit fasse la même chose que les autres, Git ne peut pas le dire, il ne peut dire si les commits sont les mêmes que s'ils ont le même ID de commit (SHA-1) . Donc, une fois que vous l'avez écrasé, vous devez dire à git de supprimer l'ancienne branche avec git branch -D topicpour la supprimer de force.
Brian Campbell

66

C'est la façon dont je suis généralement de combiner plusieurs Commits en un seul commit avant de pousser le code.

Pour y parvenir, je vous suggère d'utiliser le concept de « squash » fourni par GIT.

Suivez les étapes ci-dessous.

1) git rebase -i master (au lieu de master, vous pouvez également utiliser un commit spécifique)

ouvrez l'éditeur interactif rebase, où il affichera tous vos commits. Fondamentalement, où vous devez identifier les commits que vous souhaitez fusionner en un seul commit.

Imaginez que ce sont vos commits et que vous montriez quelque chose comme ça dans l'éditeur.

pick f7f3f6d changed my name a bit    
pick 310154e updated README formatting and added blame   
pick a5f4a0d added cat-file  

Il est important de noter que ces validations sont répertoriées dans l'ordre opposé à celui que vous voyez normalement à l'aide de la commande log. Cela signifie que l'ancien commit sera affiché en premier.

2) Remplacez «pick» par «squash» pour les dernières modifications validées. quelque chose comme indiqué ci-dessous. Ce faisant, vos 2 derniers commits seront fusionnés avec le premier.

pick f7f3f6d changed my name a bit         
squash 310154e updated README formatting and added blame   
squash a5f4a0d added cat-file

Vous pouvez également utiliser une forme courte si vous avez beaucoup de commits à combiner:

p f7f3f6d changed my name a bit         
s 310154e updated README formatting and added blame   
s a5f4a0d added cat-file

pour l'édition, utilisez «i», cela activera l'éditeur pour l'insertion. Gardez à l'esprit que la plupart des commit (les plus anciens) ne peuvent pas être écrasés car il n'y a pas de commit précédent à combiner. Il faut donc le choisir ou «p». Utilisez 'Echap' pour quitter le mode insertion.

3) Maintenant, enregistrez l'éditeur avec la commande suivante. : wq

Lorsque vous enregistrez cela, vous avez un seul commit qui introduit les modifications des trois commits précédents.

J'espère que ceci vous aidera.


5
Cela est peut-être évident pour les autres mais, quand vous dites "git rebase -i", vous devez également spécifier à quel commit vous commencez. C'est quelque chose que je n'avais pas réalisé lorsque j'ai essayé de suivre cet exemple. Donc, dans cet exemple, ce serait "git rebase -i xxxxx" où xxxxx est le commit juste avant f7f3f6d chronologiquement. Une fois que j'ai compris cela, tout s'est déroulé exactement comme décrit ci-dessus.
nukeguy

C'est intéressant @nukeguy, je n'ai eu aucun problème à ne pas spécifier de commit spécifique. Il était juste par défaut ce qui était là.
JCrooks

Peut-être que comme @nukeguy, git rebase -i HEAD~2était un endroit utile pour moi pour commencer. Ensuite, cette réponse a été utile. Ensuite, mon a git statusmontré "Votre branche et 'origine / fonctionnalité / xyz' ont divergé, et ont respectivement 1 et 1 commits différents." J'avais donc besoin de git push origin feature/xyz --force-with-leasevoir stackoverflow.com/a/59309553/470749 et freecodecamp.org/forum/t
Ryan

11

Premièrement : rien ne vous dit de n'avoir qu'un seul commit par branche et par push: un push est un mécanisme de publication vous permettant de publier un historique local (c'est-à-dire une collection de commits) sur un repo distant.

Deuxièmement : un git merge --no-ff topical_xFeatureenregistrement sur master comme un seul commit de votre travail de sujet, avant de pousser master.
(De cette façon, vous restez topical_xFeaturedans les parages pour d'autres évolutions, que vous pouvez enregistrer en mastertant que nouveau commit unique lors de la prochaine fusion - no-ff.
Si se débarrasser topical_xFeatureest l'objectif, alors git merge --squashest la bonne option, comme détaillé dans Brian Campbell de » réponse .)


Je pense que ce n'est --squashpas --no-ffce que tu veux. --no-ffcréerait un commit de fusion, mais laisserait également tous les commits de topical_xFeature.
Brian Campbell

@Brian: Je suis d'accord et j'ai voté pour votre réponse, mais j'ai d'abord pensé à l'option --no-ff parce que je voulais garder la topical_featurebranche autour, et juste enregistrer un seul commit sur la masterbranche.
VonC le

8

Passez à la branche principale et assurez-vous que vous êtes à jour.

git checkout master

git fetch cela peut être nécessaire (en fonction de votre configuration git) pour recevoir des mises à jour sur origin / master

git pull

Fusionnez la branche de fonctionnalité dans la branche principale.

git merge feature_branch

Réinitialisez la branche principale à l'état d'origine.

git reset origin/master

Git considère désormais toutes les modifications comme des modifications non organisées. Nous pouvons ajouter ces modifications en un seul commit. Ajouter . ajoutera également des fichiers non suivis.

git add --all

git commit

Réf: https://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit


3
cette réponse est facile à suivre et très facile à visualiser.
jokab

6
  1. Choisissez d'abord le commit après lequel vous voulez que tout vienne.

    git reflog
    5976f2b HEAD@{0}: commit: Fix conflicts
    80e85a1 HEAD@{1}: commit: Add feature
    b860ddb HEAD@{2}: commit: Add something
    
  2. Réinitialiser à votre tête sélectionnée (j'ai choisi HEAD@{2})

    git reset b860ddb --soft
    
  3. git status (juste pour être sûr)

  4. Ajoutez votre nouveau commit

    git commit -m "Add new commit"
    

Remarque: HEAD@{0}& HEAD@{1}Sont maintenant fusionnés en 1 commit, cela peut également être fait pour plusieurs commits.

git reflog à nouveau devrait afficher:

git reflog
5976f2b HEAD@{0}: commit: Add new commit
b860ddb HEAD@{1}: commit: Add something

0

Un outil pour automatiser plusieurs commits en un seul

comme le dit Kondal Kolipaka . Utilisation de "git rebase -i"

La logique de "git rebase"

Lorsque vous utilisez "git rebase -i", git génère le fichier git-rebase-todo dans le répertoire actuel .git / rebase-merge, puis appelle l'éditeur git pour permettre aux utilisateurs de modifier le fichier git-rebase-todo pour le traitement. L'outil doit donc répondre:

  1. Modifiez l'éditeur git à l'outil que nous avons fourni;
  2. L'outil traite le fichier git-rebase-todo.

Modifier l'éditeur git par défaut

git config core.editor #show current default git editor
git config --local --replace-all  core.editor NEW_EDITOR # set the local branch using NEW_EDITOR as git editor

Donc, l'outil doit changer l'éditeur git et traiter le fichier git-rebase-todo. L'outil utilisant python ci-dessous:

#!/usr/bin/env python3
#encoding: UTF-8

import os
import sys

def change_editor(current_file):
    os.system("git config --local --replace-all  core.editor " + current_file) # Set current_file as git editor
    os.system("git rebase -i") # execute the "git rebase -i" and will invoke the python file later with git-rebase-todo file as argument
    os.system("git config --local --replace-all core.editor vim") # after work reset the git editor to default

def rebase_commits(todo_file):
    with open(todo_file, "r+") as f:
        contents = f.read() # read git-rebase-todo's content
        contents = contents.split("\n")
        first_commit = True
        f.truncate()
        f.seek(0)
        for content in contents:
            if content.startswith("pick"):
                if first_commit:
                    first_commit = False
                else:
                    content = content.replace("pick", "squash") # replace the pick to squash except for the first pick
            f.write(content + "\n")

def main(args):
    if len(args) == 2:
        rebase_commits(args[1]) # process the git-rebase-todo
    else:
        change_editor(os.path.abspath(args[0])) # set git editor

if __name__ == "__main__":
    main(sys.argv)

Réf: https://liwugang.github.io/2019/12/30/git_commits_en.html


4
Veuillez atténuer la promotion de votre site Web. Voir aussi Comment ne pas être un spammeur.
tripleee
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.