2009-09-23 4 views
23

J'essaie d'avoir deux branches avec des fichiers binaires en git - un "développement" et un "stable". La branche de développement peut avoir plusieurs changements de ces fichiers avant de les "relâcher" à la branche stable (et la branche stable a ces fichiers renommés, au cas où cela serait pertinent).Git fusionne à plusieurs reprises le squash

Je pourrais faire une fusion normale, cela fonctionne très bien, mais préserve trop d'historique - en tirant la branche "stable", tous les commits intermédiaires de la branche "développement" sont tirés aussi (parce qu'ils sont parents commits) . Mais nous parlons de fichiers binaires qui n'ont pas de stratégie de fusion sensée (sauf la leur/la nôtre), donc l'historique réel des fichiers sur la branche de développement est inutile. Quand je tire la branche « stable », je reçois ceci:

 
      X-------------------G stable 
     /    /
    a---b---c---d---e---f---g development 

Parce que G a un parent dans la branche de développement, je reçois toute l'histoire de la branche de développement (objets de données pour c, d, e, f et g) dans mon dépôt, ce qui ne m'intéresse pas (X est le même que b, avec un certain changement de nom de fichier appliqué).

J'ai donc essayé de modifier git merge --squash de la branche de développement vers la branche stable. La première fusion et engagement se sont OK, les résultats ont été comme prévu (nice journal des modifications dans le message de commit, aucun rapport avec la branche de développement):

   X-------------------G stable 
     /     
    a---b---c---d---e---f---g development

Après je tire cette squashed branche stable, je reçois cela en mon dépôt, qui est ce que je veux:

 
a---b---X---G 

Mais la deuxième fusion a échoué (car git avait aucun moyen de savoir combien je déjà fusionné et suis devenu confus).

  • Est-il possible d'enregistrer en quelque sorte la fusion, sans produire un "commit de fusion" avec deux parents?
  • Ou, est-il possible de dire à git de ne fusionner qu'une certaine "plage" de révisions, comme dans SVN?
  • Ou, est-il possible de faire une fusion normale sans avoir à télécharger toutes les références de l'autre branche lors du tirage?
  • Ou devrais-je fournir un pilote de fusion personnalisé pour les fichiers en question, qui renomme simplement "leur" version en "notre", résolvant ainsi les conflits? J'ai toujours peur que --squash essaye toujours de fusionner toute l'histoire, jusqu'au parent commun, résolvant seulement la moitié de mon problème.

Mise à jour: rebasage

Si je comprends rebasage correctement, je vais finir avec ceci:

 
           X stable 
          /
    a---b---c---d---e---f---g development 

Ce qui me reçoit toutes les données que je ne suis pas intéressé (c , d, e, f) et en prime, je perds l'information que b était une version stable dans la branche. Chaque révision de développement ajoute environ 5 Mo à la taille du dépôt (et le remballage de tout le rapport ne le réduit que d'environ 10%), la branche "stable" est presque gratuite (les données sont déjà là). Je veux tirer une seule nouvelle révision de la branche stable pour tirer seulement le nouveau 5MB, mais au lieu de mettre à jour de X à G télécharge 25MB, parce que je ne suis pas en mesure de dire que je ne me soucie pas du contenu de c, d , e et f.

Répondre

23

A partir de

 X stable 
    /     
a---b---c---d---e---f---g development 

Vous pouvez utiliser les étapes suivantes pour copier la dernière livraison de votre branche de développement à votre branche stable:

git checkout [email protected]{0} # get working tree from "development", detach HEAD 
git reset --soft stable # reposition detached HEAD on "stable" 
git commit # enter the appropriate commit message 
git branch temp # create a temporary branch "temp" at HEAD 
git checkout temp # get on the new temporary branch 
git branch -M stable # rename "temp" to "stable" 

si vous vous avec:

 X-------------------G stable 
    /     
a---b---c---d---e---f---g development 

Si vous continuer à travailler sur le « développement », par exemple,

 X-------------------G stable 
    /     
a---b---c---d---e---f---g---h---i---j development 

vous pouvez répéter la même commande git comme ci-dessus et vous finirez avec:

 X-------------------G-----------J stable 
    /     
a---b---c---d---e---f---g---h---i---j development 
+1

Cela semble très bien, mais je ne comprends pas exactement pourquoi il est nécessaire de créer une branche temporaire, puis le renommer. Est-ce parce que vous êtes encore détaché après le commit? –

+0

@ kim-sullivan: le "exactement pourquoi": quand vous faites le commit dans l'exemple ci-dessus, il déplace "HEAD" mais pas "stable" pour pointer vers le nouveau commit - la tête est toujours "détachée", et ' stable' pointe toujours vers la version précédente (X, dans votre exemple original). Faire la branche et renommer (le checkout est techniquement optionnel, mais sans cela vous devrez fournir des arguments à 'git branch -M', et vous serez toujours détaché, ce qui est probablement non souhaitable) à J (par la réponse). – lindes

+4

En outre: Vous pouvez trouver 'git log --graph --oneline --all --decorate' et' git status' à chaque point de cette série de commandes pour être informatif. Et/ou en regardant le contenu de divers fichiers dans .git /, peut-être avec 'grep. .git/HEAD .git/refs/heads/* ' – lindes

7

Ce n'est pas le bon endroit pour utiliser merge --squash.Un bon endroit pour l'utiliser est dans une branche de sujet jetable, que vous allez fusionner dans votre branche principale, puis vous en débarrasser. Tout le développement effectué dans la branche du sujet est affiché comme une validation dans la branche principale. Dans votre situation, vous devez normalement fusionner à partir de la branche de développement, puis utiliser git-rebase --interactive pour écraser les validations souhaitées.

Est-il possible d'enregistrer en quelque sorte la fusion, sans produire une « fusion Commit » avec deux parents?

Si je comprends bien la question, non. Absolument pas.

Est-il possible de dire git fusionner seulement une certaine « gamme » de révisions, comme dans SVN?

Oui. git merge [commit hash]

Ou, est-il possible de faire une fusion normale sans avoir à télécharger tous les refs de l'autre branche en tirant?

Voir la réponse à la question précédente.

Ou dois-je fournir un pilote de fusion personnalisé pour les fichiers en question, qui renomme simplement « leur » version à « notre », résolvant ainsi les conflits? J'ai toujours peur que --squash essaye toujours de fusionner toute l'histoire, jusqu'au parent commun, en ne résolvant que la moitié de mon problème.

Non! N'utilisez simplement pas git merge --squash. Ce n'est pas le bon endroit pour l'utiliser!

+0

Merci pour votre réponse! Je ne suis pas sûr que le rebasage est ce dont j'ai besoin. J'ai besoin d'enregistrer de l'historique dans la branche "stable", donc je peux aller et venir entre les "releases".C'est ce que je veux: ' D -> S D | D | D -> S D | D -> S' Mais j'ai seulement besoin de l'histoire de la branche S - je me fiche de l'histoire de la branche D. Le problème que je dois résoudre est comment tirer la branche S sans tirer toutes les références parents de la branche D (si je comprends bien, rebasing se débarrasse de tous les commits S qui n'est pas ce que je veux). ' D -> x D D D -> x D D -> S' –

+0

Oui, je comprends que vous devez préserver l'histoire actuelle de la branche S. Non, le rebasage ne supprime pas tous les commits S antérieurs; avant de fusionner le développement en stable, notez le [commit hash] puis git rebase -i [commit hash] - alors vous écraserez uniquement les commits que vous venez de fusionner. Aussi, en ce qui concerne les versions, pourquoi ne pas simplement marquer des commits spécifiques comme des versions? – artagnon

+0

Je suis désolé, je ne comprends pas très bien votre D -> S D | D diagramme trop bien. J'espère que mon commentaire précédent a tout clarifié. – artagnon

4

vous pouvez utiliser git rebase -i (mode interactif) du squash tous vos changements, il suffit de changer les lignes à m ou meld (mise à jour: pour les nouveaux Git les rejets de type s ou squash)

vous que d'avoir un seul commettras qui vous pouvez fusionner.si vous voulez que chaque étape soit réservée, vous devez créer une autre branche sur votre branche de piratage avant de faire la rebase, ceci peut être fait avec git branch small-dirty-changes-i-don’t-want-anybody-to-see

tout ceci doit être fait avant d'essayer de fusionner; étape par étape:

# on branch "hacking": hack commit hack commit hack commit 

# create a reference to your history 
$ git branch dirty-history 

# sqash your commits by setting all lines to "meld" 
$ git rebase -i 

# checkout master or the branch you want to merge to 
$ git checkout master 

# merge your squashed commit 
$ git merge hacking 
# or: $ git pull hacking 
+0

Hm, je pourrais probablement rebasculer les changements sur la branche de développement ("dirty") de temps en temps (en fait, avant de fusionner en stable et libérant), donc je n'ai pas tous ces binaires intermédiaires dont je me fiche . La seule chose importante entre les versions stables est le hachage et les messages de validation ... Le hachage est important pour les tests (quelle version a été testée et laquelle ne l'était pas). Je vais devoir y réfléchir un peu plus. –

+0

bonne réponse, je suppose que "fusion" signifie de nos jours "squash" (votre réponse est à partir de 2009!) –

+1

Oui, c'est maintenant 's' /' squash'. – knittl

-1

Je suis nouveau à git, mais il semble que --squash est ce que vous voulez, vous avez juste besoin de gérer la branche différemment. Cela aurait pour effet de tronquer l'historique de la branche de développement, et la fusion suivante ne serait que ce que vous vouliez. En regardant la page man, il semble que git branch -f development pourrait faire la même chose que la suppression et la recréation de la branche. Comme je l'ai dit, je suis assez nouveau pour git, donc j'aimerais avoir des commentaires sur cette idée.

0

Le top voted answer diffère efficacement les branches stable et de développement et applique toutes les modifications manquantes dans l'écurie comme une validation. Il existe une autre méthode pour ce faire:

$ git checkout stable 
$ git diff stable development | git apply --index - 
$ git commit 

Avant de valider, vous pouvez consulter les fichiers intermédiaires. Si vous souhaitez annuler le processus après avoir appliqué le diff, vous pouvez simplement utiliser les commandes de base pour supprimer les modifications de l'index et du répertoire de travail.

$ git reset --hard 

Vous pouvez répéter le processus autant de fois que, puisque vous êtes effectivement tout diff-ment les conseils des deux branches chaque fois que vous devez fusionner.

Notez qu'il y a ici une supposition qui s'applique à l'autre réponse: il n'y a pas de commits dans la branche stable autres que ceux qui proviennent de la branche de développement. Si la même ligne était modifiée indépendamment dans les deux branches, contrairement à une fusion régulière, cette approche ne vous donnerait pas de conflit. Au lieu de cela, il remplacerait simplement les changements dans la branche stable avec ceux en développement.

Si cela vous inquiète, à chaque fois que vous validez sur stable, vous pouvez placer la plage de hachage de validation de développement fusionnée dans le message de validation. De cette façon, vous avez un certain record, si jamais vous avez besoin de revenir en arrière.