133 votes

Est-ce que git-svn dcommit après avoir fusionné dans git est dangereux ?

Ce qui m'a motivé à essayer git-svn, c'est la fusion et la dérivation sans effort. Puis j'ai remarqué que man git-svn(1) dit :

"L'exécution de git-merge ou git-pull n'est PAS recommandée sur une branche à partir de laquelle vous prévoyez de faire un dcommit. Subversion ne représente pas les fusions d'une manière raisonnable ou utile ; ainsi les utilisateurs utilisant Subversion ne peuvent pas voir les fusions que vous avez faites. De plus, si vous fusionnez ou tirez d'une branche git qui est un miroir d'une branche SVN, dcommit peut commettre sur la mauvaise branche."

Cela signifie-t-il que je ne peux pas créer une branche locale à partir de svn/trunk (ou d'une branche), faire des modifications, fusionner à nouveau dans svn/trunk, puis dcommiter ? Je comprends que les utilisateurs de svn verront le même désordre que les fusions dans svn avant 1.5.x ont toujours été, 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 ?

174voto

Sebastien Varrette Points 2277

En fait, j'ai trouvé un moyen encore plus efficace avec la méthode --no-ff sur git merge. Toute cette technique de squash que j'utilisais auparavant n'est plus nécessaire.

Mon nouveau flux de travail est maintenant le suivant :

  • J'ai une branche "master" qui est la seule branche à partir de laquelle je commets et qui clone le dépôt SVN ( -s supposez que vous avez une mise en page SVN standard dans le dépôt trunk/ , branches/ y tags/ ):

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

    git checkout -b work
  • commettre localement dans la branche "travail" ( -s pour signer votre message de livraison). Dans la suite, je suppose que vous avez fait 3 commits locaux

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

Maintenant vous voulez commiter sur le serveur SVN

  • [Eventuellement] cacher les modifications que vous ne voulez pas voir commiter 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 focaliser sur une fonctionnalité donnée)

    (work)$> git stash
  • rebase la branche master avec le dépôt SVN (pour mettre à jour depuis le serveur SVN)

    (work)$> git checkout master
    (master)$> git svn rebase
  • retournez à la branche de travail et rebasez avec master

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

    (work)$> git log --graph --oneline --decorate
  • Maintenant, il est temps de fusionner les trois commits de la branche "work" dans "master" en utilisant cette merveilleuse méthode. --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 modifier ( amend ) le dernier commit pour vos gars du SVN (sinon ils ne verront qu'un seul commit avec le message "Merge branch 'work'".

    (master)$> git commit --amend
  • Enfin, commit 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

49voto

Greg Hewgill Points 356191

La création de branches locales est tout à fait 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 fais des commits. Si je suis en train de travailler, je crée une branche topic et je travaille dessus. Quand je veux la commiter, je fais ce qui suit :

  1. Transférer tout sur la branche sujet
  2. git svn rebase (résoudre tout conflit entre votre travail et svn)
  3. git checkout master
  4. git svn rebase (cela fait de l'étape suivante une fusion rapide, voir les commentaires d'Aaron ci-dessous)
  5. git merge topic_branch
  6. résoudre les éventuels conflits de fusion (il ne devrait pas y en avoir à ce stade)
  7. git svn dcommit

J'ai également une autre situation où je dois maintenir certaines modifications locales (pour le débogage) qui ne devraient jamais être poussées 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 branchées à partir de work. Lorsque je veux commiter du travail ici, je vérifie master et utilise cherry-pick pour choisir les commits de la branche work que je veux commiter dans svn. Ceci parce que je veux éviter de commiter les trois commits de changement local. Ensuite, je committe depuis la branche master et rebase tout.

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

Je pense qu'il doit y avoir un meilleur moyen de fusionner la modification sur une branche topic tout en sautant les commits de modification locale que d'utiliser cherry-pick, donc si quelqu'un a une idée, elle sera la bienvenue.

33voto

Yaakov Belch Points 2489

Une solution simple : Supprimer la branche "travail" 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 ' travail de fusion git avec git branch -d work ' pour supprimer la branche de travail temporaire.

Explication du contexte : Le problème de la fusion/du commit 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 préservé, mais le fait que ce contenu provenait (partiellement) d'une autre branche spécifique est perdu. Voir : Pourquoi git svn dcommit perd-il l'historique des commits de fusion pour les branches locales ?

(Note : 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 dépôt svn, cette information de fusion ne peut être représentée d'aucune manière).

Mais c'est le ensemble du site problème. Si vous supprimez la branche 'work' après qu'elle ait été fusionnée dans la branche 'master', votre dépôt git est 100% propre et ressemble exactement à votre dépôt svn.

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

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

Tout le travail se fait alors dans le "local-directory". Lorsque j'ai besoin d'obtenir des mises à jour du serveur (comme 'svn update'), je le fais :

$> git checkout master
$> git svn rebase

Je fais tout mon travail de développement dans une branche séparée 'work' qui est créée comme ceci :

$> git checkout -b work

Bien sûr, vous pouvez créer autant de branches que vous le souhaitez pour votre travail et fusionner et rebaser entre elles comme vous le souhaitez (il suffit de les supprimer lorsque vous en avez fini avec elles --- comme indiqué ci-dessous). Dans mon travail normal, je commets très fréquemment :

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

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

$> git log

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

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

Il suffit de coller le hachage sha-1 de notre dernier commit svn au lieu du mien. Vous pouvez lire la documentation avec 'git help rebase' pour les détails. En bref : cette commande ouvre d'abord un éditeur présentant vos commits ----. Changez simplement 'pick' en 'squash' pour tous les commits que vous voulez écraser avec les commits précédents. Bien sûr, la première ligne doit rester un 'pick'. De cette façon, vous pouvez condenser vos nombreux petits commits en une ou plusieurs unités significatives. Sauvegardez et quittez l'éditeur. Vous obtiendrez un autre éditeur vous demandant de réécrire les messages du journal de commit.

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

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

$> git checkout master
$> git svn rebase

Maintenant nous sommes de retour à l'ancienne branche 'master' mise à jour avec tous les changements qui ont eu lieu entre-temps dans le dépôt svn (vos nouveaux changements sont cachés dans la branche 'work').

Si certaines modifications risquent d'entrer en conflit avec les modifications de votre nouveau "travail", vous devez les résoudre localement avant de pouvoir pousser votre nouveau travail (voir les détails plus loin). Ensuite, nous pouvons pousser nos changements 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

Note 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.

Note 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 faites 'git svn dcommit', git oublie que votre branche a été fusionnée avec 'master'. Vous devez la 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 "travail" pour éviter de pirater accidentellement la branche "master" :

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

Intégrer votre "travail" avec les changements sur svn : Voici ce que je fais lorsque 'git svn rebase' révèle que d'autres personnes ont modifié 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 n'utilise les pouvoirs de git qu'à chaque tour de " update/hack/dcommit " --- mais laisse l'historique du projet à long terme tout aussi linéaire que le dépôt svn. Cela convient si vous voulez simplement commencer à utiliser les fusions git par petites étapes dans un projet svn hérité.

Lorsque vous serez plus familier avec la fusion git, n'hésitez pas à explorer d'autres flux de travail : Si vous savez ce que vous faites, vous puede mélanger les fusions git avec les fusions svn ( Utiliser git-svn (ou similaire) juste pour aider à la fusion svn ? )

8voto

Marius K Points 272

La réponse de Greg Hewgill au sommet 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 accélérée.

On peut s'en assurer en utilisant le drapeau "--ff-only" dans git-merge, mais je ne lance généralement pas "git svn rebase" dans la branche, seulement "git rebase master" sur celle-ci (en supposant qu'il s'agit seulement d'une branche locale). Ensuite, un "git merge thebranch" est garanti en avance rapide.

6voto

luntain Points 1779

Une manière sûre de fusionner les branches svn dans git est d'utiliser git merge --squash. Cela créera un seul commit et s'arrêtera pour que vous puissiez ajouter un message.

Disons que vous avez une branche svn à thème, 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 regroupées en un seul commit qui attend dans l'index.

git commit

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X