web-dev-qa-db-fra.com

Pourquoi Squash Git s'engage pour les demandes de tirage?

Pourquoi chaque dépôt Github sérieux que j'effectue des requêtes veut-il que j'écrase mes commits en un seul commit?

Je pensais que le journal git était là pour que vous puissiez inspecter toute votre histoire et voir exactement quels changements se sont produits où, mais l'écraser le retire de l'historique et le regroupe en un seul commit. Dans quel but?

Cela semble également aller à l’encontre du mantra "s’engager tôt et s’engager souvent".

221
hamstar

Pour que vous ayez un historique Git clair et concis qui documente clairement et facilement les changements effectués et les raisons pour lesquelles.

Par exemple, un journal git "non écrasé" typique pour moi pourrait ressembler à ceci:

7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
787g8fgf78... hotfix for #5849564648
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Quel bordel!

Alors qu'un journal git plus soigneusement géré et fusionné avec un peu plus d'attention sur les messages pour cela pourrait ressembler à:

7hgf8978g9... 8483948393 Added new slideshow feature
787g8fgf78... 5849564648 Hotfix for Android display issue
9080gf6567... 6589685988 Implemented pop-up to select language

Je pense que vous pouvez voir le point de squashing commits en général et le même principe s'applique aux pull demandes - Lisibilité de l'historique. Vous pouvez également ajouter à un journal de validation qui contient déjà des centaines, voire des milliers de validations, ce qui contribuera à garder l'histoire croissante courte et concise.

Vous voulez vous engager tôt et souvent. C'est une meilleure pratique pour plusieurs raisons. Je trouve que cela m'amène à avoir fréquemment des commits qui sont "wip" (travail en cours) ou "partie A terminée" ou "faute de frappe, correction mineure" où j'utilise git pour m'aider à travailler et me donner des points de travail qui Je peux revenir en arrière si le code suivant ne fonctionne pas au fur et à mesure que je progresse pour que les choses fonctionnent. Cependant, je n'ai pas besoin ou je ne veux pas que cette histoire fasse partie de l'historique final de Git, donc je peux écraser mes commits - mais voir les notes ci-dessous pour savoir ce que cela signifie sur une branche de développement vs maître.

S'il existe des jalons majeurs qui représentent des étapes de travail distinctes, il est toujours correct d'avoir plus d'un commit par fonctionnalité/tâche/bogue. Cependant, cela peut souvent mettre en évidence le fait que le ticket en cours de développement est "trop gros" et doit être décomposé en petits morceaux qui peuvent être autonomes, par exemple:

8754390gf87... Implement feature switches

ressemble à "1 pièce". Soit ils existent, soit ils n'existent pas! Cela ne semble pas logique de l'éclater. Cependant, l'expérience m'a montré que (selon la taille et la complexité de l'organisation) un chemin plus granulaire pourrait être:

fgfd7897899... Add field to database, add indexes and a trigger for the dw group
9458947548g... Add 'backend' code in controller for when id is passed in url.
6256ac24426... Add 'backend' code to make field available for views.
402c476edf6... Add feature to UI

De petits morceaux signifient des révisions de code plus faciles, des tests unitaires plus faciles, une meilleure opportunité pour l'AQ, un meilleur alignement sur le principe de responsabilité unique, etc.

Pour les détails pratiques sur le moment de faire de telles courges, il existe essentiellement deux étapes distinctes qui ont leur propre flux de travail

  • votre développement, par ex. tirer des demandes
  • votre travail ajouté à la branche principale, par exemple Maître

Au cours de votre développement, vous vous engagez "tôt et souvent" et avec des messages rapides "jetables". Vous voudrez peut-être écraser ici parfois, par exemple écraser dans les messages de validation et de tâches à effectuer. Il est correct, au sein de la branche, de conserver plusieurs validations qui représentent des étapes distinctes que vous avez effectuées dans le développement. La plupart des courges que vous choisissez de faire doivent se trouver dans ces branches de fonctionnalités, pendant leur développement et avant d'être fusionnées pour les maîtriser.
Lors de l'ajout à la branche de la ligne principale, vous souhaitez que les validations soient concises et correctement formatées en fonction de l'historique existant de la ligne principale. Cela peut inclure l'ID du système Ticket Tracker, par exemple JIRA comme illustré dans les exemples. Le squashing ne s'applique pas vraiment ici, sauf si vous souhaitez "cumuler" plusieurs validations distinctes sur le maître. Normalement non.

En utilisant --no-ff lors de la fusion vers master utilisera un commit pour la fusion et préservera également l'historique (dans la branche). Certaines organisations considèrent qu'il s'agit d'une meilleure pratique. Voir plus sur https://stackoverflow.com/q/9069061/631619 Vous verrez également l'effet pratique dans git log où un --no-ff commit sera le dernier commit, en haut de HEAD (quand cela vient d'être fait), alors que sans --no-ff il peut être plus bas dans l'histoire, selon les dates et autres validations.

168
Michael Durrant

Parce que souvent la personne qui tire un PR se soucie de l'effet net des commits "fonctionnalité ajoutée X", pas des "modèles de base, fonction de correction de bogues X, fonction d'ajout Y, corrections de fautes dans les commentaires, paramètres de mise à l'échelle des données ajustés, hashmap fonctionne mieux que liste "... niveau de détail

Si vous pensez que vos 16 commits sont mieux représentés par 2 commits plutôt que par 1 "Ajout de la fonctionnalité X, re-factorisé Z pour utiliser X", alors c'est probablement bien de proposer un pr avec 2 commits, mais alors il pourrait être préférable de proposer 2 pr séparés dans ce cas (si le repo insiste toujours sur des pr single commit)

Cela ne va pas à l'encontre du mantra "commit early and commit souvent", comme dans votre référentiel, pendant que vous développez, vous avez toujours les détails granulaires, donc vous avez un minimum de chances de perdre du travail, et d'autres personnes peuvent réviser/tirer/proposer pr est contre votre travail pendant que le nouveau pr est en cours de développement.

27
Andrew Hill

La principale raison de ce que je peux voir est la suivante:

  • L'interface utilisateur GitHub pour la fusion des demandes d'extraction actuellement (octobre 2015) ne vous permet pas de modifier la première ligne du message de validation, ce qui la force à être Merge pull request #123 from joebloggs/fix-snafoo
  • L'interface utilisateur GitHub pour parcourir l'historique des validations ne vous permet pas actuellement de visualiser l'historique de la branche à partir de --first-parent point de vue
  • L'interface utilisateur GitHub pour regarder le blâme sur un fichier actuellement ne vous permet pas de voir le blâme du fichier avec le --first-parent point de vue (notez que cela n'a été corrigé que dans Git 2.6.2, donc nous pourrions pardonner à GitHub de ne pas l'avoir disponible)

Ainsi, lorsque vous combinez ces trois situations ci-dessus, vous obtenez une situation où les validations non compressées fusionnées semblent laides à partir de l'interface utilisateur GitHub.

Votre histoire avec les validations écrasées ressemblera à quelque chose

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature
56556316ad... Merge pull request #324 from ahacker/fix-Android-display
787g8fgf78... Hotfix for Android display issue
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... Implemented pop-up to select language

Alors que sans commit écrasé, l'histoire ressemblera à quelque chose

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... Merge pull request #324 from ahacker/fix-Android-display
787g8fgf78... hotfix for #5849564648
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Lorsque vous avez beaucoup de commits dans un suivi PR où un changement est entré peut devenir un peu un cauchemar si vous vous limitez à utiliser l'interface utilisateur GitHub.

Par exemple, vous trouvez un pointeur nul étant dé-référencé quelque part dans un fichier ... vous dites donc "qui a fait cela, et quand? Quelles versions sont affectées?". Ensuite, vous vous dirigez vers la vue de blâme dans l'interface utilisateur de GitHub et vous voyez que la ligne a été modifiée dans 789fdfffdf... "oh mais attendez une seconde, cette ligne venait juste d'être modifiée pour s'adapter au reste du code", alors maintenant vous devez naviguer vers l'état d'arborescence pour ce fichier dans le commit parent et re -visitez la page du blâme ... finalement vous trouvez le commit ... c'est un commit d'il y a 6 mois ... "oh **** cela pourrait affecter les utilisateurs pendant 6 mois" vous dites ... ah mais attendez, ce commit était en fait dans une Pull Request et n'a été fusionné qu'hier et personne n'a encore coupé une version ... "Damn you people for merging commits without squashing history" est le cri qui peut généralement être entendu après environ 2 ou 3 expéditions d'archéologie de code via l'interface utilisateur GitHub

Voyons maintenant comment cela fonctionne si vous utilisez la ligne de commande Git (et super-génial 2.6.2 qui a le correctif pour git blame --first-parent)

  • Si vous utilisiez la ligne de commande Git, vous seriez en mesure de contrôler complètement le message de validation de fusion et donc la validation de fusion pourrait avoir une ligne de résumé Nice.

Donc, notre historique de commit ressemblerait

$ git log
1256556316... #423 Added new slideshow feature
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... #324 Hotfix for Android display issue
787g8fgf78... hotfix for #5849564648
f56556316e... #28 Implemented pop-up to select language
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Mais on peut aussi faire

$ git log --first-parent
1256556316... #423 Added new slideshow feature
56556316ad... #324 Hotfix for Android display issue
f56556316e... #28 Implemented pop-up to select language

(En d'autres termes: la CLI Git vous permet d'avoir votre gâteau et de le manger aussi)

Maintenant, quand nous rencontrons le problème du pointeur nul ... eh bien nous utilisons simplement git blame --first-parent -w dodgy-file.c et nous recevons immédiatement le commit exact où la dé-référence du pointeur nul a été introduite dans la branche master en ignorant les simples changements d'espaces.

Bien sûr, si vous effectuez des fusions à l'aide de l'interface utilisateur GitHub, alors git log --first-parent est vraiment merdique grâce à GitHub forçant la première ligne du message de validation de fusion:

1256556316... Merge pull request #423 from jrandom/add-slideshows
56556316ad... Merge pull request #324 from ahacker/fix-Android-display
f56556316e... Merge pull request #28 from somwhere/select-lang-popup

Donc, pour résumer une longue histoire:

L'interface utilisateur de GitHub (octobre 2015) présente un certain nombre de lacunes concernant la manière dont elle fusionne les demandes d'extraction, la façon dont elle présente l'historique des validations et la façon dont elle attribue les informations de blâme. La meilleure façon actuelle de contourner ces défauts dans l'interface utilisateur de GitHub est de demander aux utilisateurs d'annuler leurs commits avant de fusionner.

La CLI Git n'a pas ces problèmes et vous pouvez facilement choisir la vue que vous souhaitez voir afin que vous puissiez à la fois découvrir la raison pour laquelle un changement particulier a été effectué de cette façon (en consultant l'historique des validations non écrasées) ainsi que voir les commits efficacement écrasés.

Post Script

La dernière raison souvent citée pour écraser les commits est de faciliter le rétroportage ... si vous n'avez qu'un seul commit sur le port arrière (c'est-à-dire le commit écrasé), il est facile de choisir ...

Eh bien, si vous regardez l'histoire git avec git log --first-parent alors vous pouvez simplement sélectionner les commits de fusion. La plupart des gens se trompent sur les commits de fusion de la sélection des cerises car vous devez spécifier le -m N option mais si vous avez obtenu le commit de git log --first-parent alors vous savez que c'est le premier parent que vous souhaitez suivre donc ce sera git cherry-pick -m 1 ...

24
Stephen Connolly

Je suis d'accord avec les sentiments exprimés dans d'autres réponses dans ce fil sur l'affichage d'un historique clair et concis des fonctionnalités ajoutées et des bugs corrigés. Cependant, je voulais aborder un autre aspect auquel votre question a échappé mais qui n'a pas été explicitement mentionné. Une partie du blocage que vous pourriez avoir à propos de certaines des méthodes de travail de git est que git vous permet de réécrire l'historique qui semble étrange lorsqu'il est introduit dans git après avoir utilisé d'autres formes de contrôle de source où de telles actions sont impossibles. En outre, cela va également à l'encontre du principe généralement accepté du contrôle de code source: une fois que quelque chose est validé/enregistré dans le contrôle de code source, vous devriez pouvoir revenir à cet état, quelles que soient les modifications que vous apportez après ce commit. Comme vous l'indiquez dans votre question, il s'agit de l'une de ces situations. Je pense que git est un excellent système de contrôle de version; cependant, pour le comprendre, vous devez comprendre certains des détails de mise en œuvre et des décisions de conception qui se cachent derrière, et par conséquent, sa courbe d'apprentissage est plus abrupte. Gardez à l'esprit que git était destiné à être un système de contrôle de version distribué et cela aidera à expliquer pourquoi les concepteurs autorisent la réécriture de l'historique de git avec les commits de squash en étant un exemple.

2
Fred Thomsen

Je verrais si le code est rendu public ou non.

Lorsque les projets restent privés:

Je recommanderais de ne pas écraser et de voir la fabrication de la saucisse. Si vous utilisez des validations bonnes et petites, des outils comme git bisect sont très pratiques et les gens peuvent rapidement identifier les validations de régression et voir pourquoi vous l'avez fait (à cause du message de validation).

Lorsque les projets deviennent publics:

Écrasez tout, car certains commits peuvent contenir des fuites de sécurité. Par exemple, un mot de passe qui a été validé et supprimé à nouveau.

1
Vince V.

En raison de la perspective ... La meilleure pratique est d'avoir un seul commit par single <<issue-management-system>> problème si possible.

Vous pouvez avoir autant de validations que vous le souhaitez dans votre propre branche/repo de fonctionnalités, mais votre historique est pertinent pour ce que vous faites maintenant pour VOTRE perspective ... perspective à conserver dans quelques mois ...

Donc, chaque fois que vous souhaitez valider une correction de bogue ou une fonctionnalité dans un référentiel commun (cet exemple concerne la branche develop), vous pouvez le faire comme suit:

comment rebaser votre branche de fonctionnalités pour développer rapidement

    # set your current branch , make a backup of it , caveat minute precision
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # squash all your changes at once 
    git reset $(git merge-base develop $curr_branch)

    # check the modified files to add 
    git status

    # add the modified files dir by dir, or file by file or all as shown
    git add --all 

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # add the single message of your commit for the stuff you did 
    git commit -m "<<MY-ISSUE-ID>>: add my very important feature"

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # make a backup once again , use seconds precision if you were too fast ... 
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # compare the old backup with the new backup , should not have any differences 
    git diff <<old-backup>>..<<new-backup-branch>>


    # you would have to git Push force to your feature branch 
    git Push --force 
1
Yordan Georgiev