web-dev-qa-db-fra.com

Git-svn dcommit après avoir fusionné dans git est-il dangereux?

Ma motivation pour essayer git-svn est la fusion et la ramification sans effort. Puis j'ai remarqué que man git-svn (1) dit:

Exécuter git-merge ou git-pull n'est PAS recommandé sur une branche à partir de laquelle vous prévoyez de vous désengager. Subversion ne représente pas les fusions de manière raisonnable ou utile; les utilisateurs utilisant Subversion ne peuvent donc voir aucune fusion que vous avez effectuée. De plus, si vous fusionnez ou tirez d'une branche git qui est le miroir d'une branche SVN, dcommit peut s'engager dans la mauvaise branche.

Est-ce à dire que je ne peux pas créer une branche locale à partir de svn/trunk (ou une branche), pirater, fusionner à nouveau dans svn/trunk, puis annuler la validation? Je comprends que les utilisateurs de svn verront toujours le même désordre qui fusionne dans svn pre 1.5.x, mais y a-t-il d'autres inconvénients? Cette dernière phrase m'inquiète aussi. Les gens font-ils régulièrement ce genre de choses?

133
Knut Eldhuset

En fait, j'ai trouvé un moyen encore meilleur avec le --no-ff option sur git merge. Toute cette technique de squash que j'ai utilisée auparavant n'est plus nécessaire.

Mon nouveau workflow est maintenant le suivant:

  • J'ai une branche "master" qui est la seule branche à partir de laquelle je me désengage et qui clone le référentiel SVN (-s supposez que vous disposez d'une disposition SVN standard dans le référentiel trunk/, branches/, et tags/):

    git svn clone [-s] <svn-url>
    
  • Je travaille sur une branche locale "work" (-b crée la branche "work")

    git checkout -b work
    
  • valider localement dans la branche "work" (-s pour valider votre message de validation). Dans la suite, je suppose que vous avez effectué 3 commits locaux

    ...
    (work)$> git commit -s -m "msg 1"
    ...
    (work)$> git commit -s -m "msg 2"
    ...
    (work)$> git commit -s -m "msg 3"
    

Vous voulez maintenant vous engager sur le serveur SVN

  • [Finalement] cachez les modifications que vous ne voulez pas voir validées sur le serveur SVN (souvent vous avez commenté du code dans le fichier principal juste parce que vous voulez accélérer la compilation et vous concentrer sur une fonctionnalité donnée)

    (work)$> git stash
    
  • rebaser la branche principale avec le référentiel SVN (à mettre à jour depuis le serveur SVN)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • revenir à la branche de travail et rebaser avec le maître

    (master)$> git checkout work
    (work)$> git rebase master
    
  • Assurez-vous que tout va bien en utilisant, par exemple:

    (work)$> git log --graph --oneline --decorate
    
  • Il est maintenant temps de fusionner les trois validations de la branche "work" dans "master" en utilisant ce merveilleux --no-ff option

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • Vous pouvez remarquer l'état des journaux:

    (master)$> git log --graph --oneline --decorate
    * 56a779b (work, master) Merge branch 'work'
    |\  
    | * af6f7ae msg 3
    | * 8750643 msg 2
    | * 08464ae msg 1
    |/  
    * 21e20fa (git-svn) last svn commit
    
  • Maintenant, vous voulez probablement éditer (amend) le dernier commit pour vos types SVN (sinon ils ne verront qu'un seul commit avec le message "Merge branch 'work'" "

    (master)$> git commit --amend
    
  • Enfin valider sur le serveur SVN

    (master)$> git svn dcommit
    
  • Retournez au travail et récupérez éventuellement vos fichiers cachés:

    (master)$> git checkout work
    (work)$> git stash pop
    
174
Sebastien Varrette

La création de branches locales est définitivement possible avec git-svn. Tant que vous n'utilisez que des branches locales pour vous-même et que vous n'essayez pas d'utiliser git pour fusionner des branches svn en amont, tout devrait bien se passer.

J'ai une branche "master" que j'utilise pour suivre le serveur svn. C'est la seule branche à partir de laquelle je me désengage. Si je fais un peu de travail, je crée une branche thématique et je travaille dessus. Lorsque je veux le valider, je fais ce qui suit:

  1. Tout valider dans la branche thématique
  2. git svn rebase (résoudre tous les conflits entre votre travail et svn)
  3. git checkout master
  4. git svn rebase (cela fait de la prochaine étape une fusion rapide, voir les commentaires d'Aaron ci-dessous)
  5. git merge topic_branch
  6. résoudre tous les conflits de fusion (il ne devrait pas y en avoir à ce stade)
  7. git svn dcommit

J'ai également une autre situation où j'ai besoin de maintenir certains changements locaux (pour le débogage) qui ne devraient jamais être poussés vers svn. Pour cela, j'ai la branche master ci-dessus mais aussi une branche appelée "work" où je travaille normalement. Les branches thématiques sont dérivées du travail. Lorsque je veux y valider du travail, je vérifie master et utilise cherry-pick pour sélectionner les validations de la branche de travail que je souhaite valider pour svn. C'est parce que je veux éviter de valider les trois validations de changement locales. Ensuite, je me désengage de la branche principale et rebase tout.

Il vaut la peine d'exécuter git svn dcommit -n d'abord pour vous assurer que vous êtes sur le point de valider exactement ce que vous avez l'intention de valider. Contrairement à git, réécrire l'historique dans svn est difficile!

Je pense qu'il doit y avoir une meilleure façon de fusionner le changement sur une branche de sujet tout en sautant ces commits de changement local que d'utiliser la sélection de cerise, donc si quelqu'un a des idées, il serait le bienvenu.

49
Greg Hewgill

Solution simple: supprimez la branche 'work' après la fusion

Réponse courte: Vous pouvez utiliser git comme vous le souhaitez (voir ci-dessous pour un flux de travail simple), y compris la fusion. Assurez-vous simplement de suivre chaque ' git merge work ' with ' git branch -d work 'pour supprimer la branche de travail temporaire.

Explication de l'arrière-plan: Le problème de fusion/dcommit est que chaque fois que vous 'git svn dcommit' une branche, l'historique de fusion de cette branche est 'aplati': git oublie toutes les opérations de fusion qui ont été effectuées dans cette branche: seul le contenu du fichier est conservé, mais le fait que ce contenu provienne (partiellement) d'une autre branche spécifique est perdu. Voir: Pourquoi git svn dcommit perd-il l'historique des validations de fusion pour les branches locales?

(Remarque: il n'y a pas grand-chose que git-svn puisse faire à ce sujet: svn ne comprend tout simplement pas les fusions git beaucoup plus puissantes. Ainsi, à l'intérieur du référentiel svn, ces informations de fusion ne peuvent être représentées d'aucune façon.)

Mais c'est le problème entier. Si vous supprimez la branche 'work' après qu'elle a été fusionnée dans la 'branche master', votre référentiel git est 100% propre et ressemble exactement à votre référentiel svn.

Mon flux de travail: Bien sûr, j'ai d'abord cloné le référentiel svn distant dans un référentiel git local (cela peut prendre un certain temps):

$> git svn clone <svn-repository-url> <local-directory>

Tout le travail se fait alors à l'intérieur du "répertoire local". Chaque fois que j'ai besoin d'obtenir des mises à jour du serveur (comme "svn update"), je fais:

$> git checkout master
$> git svn rebase

Je fais tout mon travail de développement dans une "branche" de branche distincte qui est créée comme ceci:

$> git checkout -b work

Bien sûr, vous pouvez créer autant de branches pour votre travail que vous le souhaitez et fusionner et rebaser entre elles comme vous le souhaitez (supprimez-les simplement lorsque vous avez terminé avec elles --- comme indiqué ci-dessous). Dans mon travail normal, je m'engage très fréquemment:

$> git commit -am '-- finished a little piece of work'

L'étape suivante (git rebase -i) est facultative --- il s'agit simplement de nettoyer l'historique avant de l'archiver sur svn: une fois que j'ai atteint un mille stable que je veux partager avec les autres, je réécris l'histoire de ce `` travail '' branchez et nettoyez les messages de commit (les autres développeurs n'ont pas besoin de voir toutes les petites étapes et les erreurs que j'ai faites sur le chemin --- juste le résultat). Pour cela, je fais

$> git log

et copiez le hachage sha-1 du dernier commit qui est en direct dans le dépôt svn (comme indiqué par un git-svn-id). Ensuite j'appelle

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

Collez simplement le hachage sha-1 de notre dernier commit svn au lieu du mien. Vous voudrez peut-être lire la documentation avec 'git help rebase' pour les détails. En bref: cette commande ouvre d'abord un éditeur présentant vos validations ---- changez simplement 'pick' en 'squash' pour toutes les validations que vous souhaitez écraser avec les validations précédentes. Bien sûr, la première ligne doit rester un "choix". De cette façon, vous pouvez condenser vos nombreux petits commits en une ou plusieurs unités significatives. Enregistrez et quittez l'éditeur. Vous obtiendrez un autre éditeur vous demandant de réécrire les messages du journal de validation.

En bref: après avoir terminé le `` piratage de code '', je masse ma branche `` travail '' jusqu'à ce qu'elle regarde comment je veux le présenter aux autres programmeurs (ou comment je veux voir le travail dans quelques semaines lorsque je parcours l'historique) .

Afin de pousser les changements dans le dépôt svn, je fais:

$> git checkout master
$> git svn rebase

Maintenant, nous sommes de retour à l'ancienne branche "maître" mise à jour avec toutes les modifications intervenues entre-temps dans le référentiel svn (vos nouvelles modifications sont masquées dans la branche "travail").

S'il y a des changements qui peuvent entrer en conflit avec vos nouveaux changements de "travail", vous devez les résoudre localement avant de pouvoir pousser votre nouveau travail (voir les détails ci-dessous). Ensuite, nous pouvons pousser nos modifications vers svn:

$> git checkout master
$> git merge work        # (1) merge your 'work' into 'master'
$> git branch -d work    # (2) remove the work branch immediately after merging
$> git svn dcommit       # (3) Push your changes to the svn repository

Remarque 1: La commande 'git branch -d work' est assez sûre: elle vous permet uniquement de supprimer les branches dont vous n'avez plus besoin (car elles sont déjà fusionnées dans votre branche actuelle). Si vous exécutez cette commande par erreur avant de fusionner votre travail avec la branche 'master', vous obtenez un message d'erreur.

Remarque 2: assurez-vous de supprimer votre branche avec 'git branch -d work' entre fusion et dcommit: si vous essayez de supprimer la branche après dcommit, vous obtenez un message d'erreur: lorsque vous le faites 'git svn dcommit', git oublie que votre branche a été fusionnée avec 'master'. Vous devez le supprimer avec 'git branch -D work' qui ne fait pas le contrôle de sécurité.

Maintenant, je crée immédiatement une nouvelle branche 'work' pour éviter de pirater accidentellement la branche 'master':

$> git checkout -b work
$> git branch            # show my branches:
  master
* work

Intégration de votre 'travail' avec des changements sur svn: Voici ce que je fais quand 'git svn rebase' révèle que d'autres ont changé le dépôt svn pendant que je travaillais sur ma branche 'travail':

$> git checkout master
$> git svn rebase              # 'svn pull' changes
$> git checkout work           # go to my work
$> git checkout -b integration # make a copy of the branch
$> git merge master            # integrate my changes with theirs
$> ... check/fix/debug ...
$> ... rewrite history with rebase -i if needed

$> git checkout master         # try again to Push my changes
$> git svn rebase              # hopefully no further changes to merge
$> git merge integration       # (1) merge your work with theirs
$> git branch -d work          # (2) remove branches that are merged
$> git branch -d integration   # (2) remove branches that are merged
$> git svn dcommit             # (3) Push your changes to the svn repository

Des solutions plus puissantes existent: Le flux de travail présenté est simpliste: il utilise les pouvoirs de git uniquement dans chaque cycle de 'update/hack/dcommit' --- mais laisse l'historique du projet à long terme aussi linéaire que le référentiel svn. C'est ok si vous voulez juste commencer à utiliser les fusions git dans les petites premières étapes d'un projet svn hérité.

Lorsque vous vous familiarisez avec la fusion de git, n'hésitez pas à explorer d'autres workflows: si vous savez ce que vous faites, vous pouvez mélanger les fusion git avec les fusion svn ( tilisation de git-svn (ou similaire) juste pour aider à la fusion svn? )

33
Yaakov Belch

La réponse de Greg Hewgill en haut n'est pas sûre! Si de nouveaux commits sont apparus sur le tronc entre les deux "git svn rebase", la fusion ne sera pas rapide.

Cela peut être assuré en utilisant l'indicateur "--ff-only" pour le git-merge, mais je n'exécute généralement pas "git svn rebase" dans la branche, seulement "git rebase master" dessus (en supposant qu'il ne s'agit que d'un local branche). Ensuite, un "git merge thebranch" est garanti pour une avance rapide.

8
Marius K

Un moyen sûr de fusionner des branches svn dans git est d'utiliser git merge --squash. Cela va créer un seul commit et s'arrêter pour que vous ajoutiez un message.

Disons que vous avez une branche svn de sujet, appelée svn-branch.

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

à ce stade, toutes les modifications de la branche svn sont écrasées en un seul commit en attente dans l'index

git commit
6
luntain

Rebase la branche git locale sur la branche git principale puis dcommit et de cette façon, il semble que vous ayez fait tous ces commits en séquence afin que les utilisateurs de svn puissent le voir linéairement comme ils sont habitués. Donc, en supposant que vous ayez une branche locale appelée sujet, vous pouvez le faire

git rebase master topic

qui jouera ensuite vos commits sur la branche principale, prêt à être validé

5
JoeyJ