Dans notre équipe, nous voulons maintenir une histoire git linéaire.
Nous avons exécuté ces commandes:
l'utilisateur met à jour certains fichiers
git pull --rebase <fixup any issues> git add . git rebase --continue
Mais on voit souvent:
mkdir gitserver mkdir gitclient1 mkdir gitcleint2 # server side cd gitserver git init --bare diverge.git #client 1 cd gitclient1 git clone ../gitserver/diverge.git cd diverge <create index.html file with a few lines of text, save.> git add . git commit -m "first commit by user 1" git push #client 2 cd gitclient2 git clone ../gitserver/diverge.git cd diverge add a new line to index.html git add . git commit -m "changed line made by user 2" git push # back to client 1 $ git pull --ff-only remote: Counting objects: 3, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From D:/projects/git/git_diverge_test/gitclient1/../gitserver/diverge 7208075..ce21832 master -> origin/master fatal: Not possible to fast-forward, aborting.
Je me demande si les commandes git que j'utilise pourraient être améliorées? Par exemple, en utilisant git pull --rebase au lieu de git pull --ff-only?
Étapes à suivre pour reproduire.
créer 3 répertoires:
fatal: Not possible to fast-forward, aborting.
Pourquoi est-ce que je reçois le message fatal?
Git n'aurait-il pas dû être capable d'extraire le commit par-dessus le commit existant?
Et si je lance gitk --all, je vois que master est sur une autre 'branche' de l'arborescence de remote / origin / master.
Une approche qui fonctionne dans une certaine mesure consiste à utiliser:
git add . git commit git pull --ff-only
Est-ce que git pull --rebase est en quelque sorte plus dangereux?
3 Réponses :
Réponse courte: utilisez git pull --rebase
Tout d'abord, votre exemple ne fonctionne pas, le client 1 aurait besoin de valider autre chose avant le pull --ff-only
, mais supposons que nous l'avons fait.
git pull --ff-only
échoue? Cette commande demande à git de fetch
le dernier état de la télécommande et d'avancer rapidement la branche si possible. Donc, si nous avons des changements différents sur la branche distante et locale, cela s'arrêtera, car c'est ce que nous lui avons dit de faire avec --ff-only
git pull --rebase
? Il fetch
également, mais après cela, il fera un git rebase origin/master
. Witch prendra tous les commits sur le maître qui ne sont pas déjà sur la télécommande et les rebasera à la fin de l'origine / du maître. Bien sûr, vous devrez peut-être résoudre les conflits de fusion qui surviennent. Le seul danger que je vois est que vous devriez tester ce qui se passe si vous avez des commits de fusion dans votre branche, mais puisque vous voulez un historique linéaire, cela ne devrait pas être un problème pour vous.
Le flux de travail que vous présentez ne doit autoriser que les tirages d'avance rapide. Cependant, pour que cela fonctionne, vous avez besoin d'une coordination parfaite entre les membres de l'équipe travaillant sur différents clients et vous ne pouvez travailler que sur un client à la fois .
Par exemple, alors que quelqu'un travaille sur le client 1, les gens ne peuvent pas créer de commits sur le client 2 et vice versa. Si les commits sont créés simultanément sur les deux clients, vous vous retrouverez avec des historiques divergents qui devront être fusionnés avec un commit ou rebasés: une fusion rapide ne sera pas possible.
Ce n'est pas vraiment pratique (comme l'expérience vous l'a appris: le message d'erreur que vous obtenez et les sorties de git branch -vv
et gitk --all
vous montrent tous que vous vous êtes retrouvé avec des histoires divergentes sur les 2 clients sans le vouloir et sans vous en rendre compte il). Il s'agit également d'un flux de travail non efficace car tous les clients sauf un doivent être en attente à tout moment. Un bon flux de travail de contrôle de version devrait permettre à différentes personnes de travailler simultanément, puis d'intégrer leur travail dans le projet.
Dès que différentes personnes ont créé des commits simultanément sur différents clients, vous avez besoin de commits de fusion ou de rebases. Les validations de fusion créent des historiques non linéaires, donc si vous voulez un historique linéaire, vous devez rebaser.
Cela peut en effet être fait avec git pull --rebase
.
Le rebasage est une réécriture de l'histoire et il est dangereux de le faire sur des commits qui ont déjà été poussés vers une télécommande, mais c'est sûr de le faire sur des commits non publiés.
Lorsque vous exécutez git pull --rebase
, la branche actuelle est rebasée au-dessus de la branche amont fraîchement récupérée. Donc, c'est l'histoire de votre succursale locale qui est réécrite, pas celle de l'amont. Ainsi, tant que votre équipe n'utilise qu'une seule télécommande pour synchroniser le travail, vous pouvez fusionner les modifications de cette télécommande dans la vôtre avec git pull --rebase
sans risquer de créer des problèmes pour les autres.
Une autre alternative serait de fusionner avec git pull --ff
qui fera des fusions d'avance rapide lorsque cela est possible, mais créera des commits de fusion lorsque ce n'est pas possible, puis réécrira l'historique du projet de temps en temps pour le rendre linéaire. Ces réécritures de l'histoire pourraient être coordonnées au niveau de l'équipe, bloquant toute activité du projet lorsqu'elles surviennent, pour éviter tout problème.
Une façon de le faire serait d'utiliser git reset
comme cela est présenté dans la section Squashing près du bas de cette section du Pro Git Book.
Cependant, un inconvénient de cette méthode est que vous perdrez les petites étapes introduites par chaque commit. C'est aussi une approche beaucoup plus tortueuse et cela peut être dangereux si quelqu'un ne respecte pas le gel de l'activité lors de la réécriture de l'histoire (sans oublier que de tels gels ne sont pas efficaces).
En conclusion, utiliser git pull --rebase
est probablement la meilleure solution ici.
Vous pouvez définir ceci comme comportement par défaut pour git pull
avec:
git config --global pull.rebase true
(Supprimez --global
si vous ne le souhaitez que pour un projet).
Pour placer votre travail local au-dessus de la branche distante: utilisez git pull --rebase
.
S'il s'agit du flux de travail prévu pour toute votre équipe: que tout le monde soit défini
git fetch # will update all remote branches git fetch origin branch # if you want to update one single branch from the remote
et git pull
se traduira maintenant par git pull --rebase
.
L'utilisation prévue de git pull --ff-only
est:
Si vous avez le travail local sur le dessus de la branche, git pull --ff-only
ne fera jamais l' action:
Si vous souhaitez importer la branche distante pour vérifier si elle a été mise à jour: utilisez git fetch
git config pull.rebase true
Après avoir exécuté git fetch
, vous pouvez comparer votre branche et origin/branch
(utilisez un visualiseur git, ou git log --graph --oneline HEAD origin/branch
), vous pouvez rebaser votre travail au-dessus de l' origin/branch
(en exécutant: git rebase origin/branch
), etc ...
Quel est le problème avec une traction régulière?
@evolutionxbox Je pense que l'idée est de maintenir un historique git linéaire, c'est-à-dire pas de branches fusionnées. Je pense que git pull s'il échoue pourrait provoquer une histoire non linéaire. Bien que je puisse me tromper là-bas.
Pourquoi une histoire linéaire est-elle souhaitée? (curieux) --- Aussi, quel est le problème à propos de l'utilisation de
--rebase
? Comment penser que cela pourrait être plus dangereux?@evolutionxbox Je ne connais pas la raison de la décision.
Qu'obtenez-vous si vous exécutez
git branch -vv
partir du client 1 et du client 2?@prosoitos user1: $ git branch -vv -> * master 3e32b6a [origin / master] une 3e nouvelle ligne ajoutée par l'utilisateur 1
@prosoitos user2: $ git branch -vv -> * master fff89e8 [origin / master: ahead 1, behind 1] une 3ème nouvelle ligne ajoutée par l'utilisateur
J'ai essayé tes pas pour reproduire. Ils ne se reproduisent pas. Vous devez avoir commis quelque chose d'autre dans client1 après le clonage de client2, et obligé client1 à avoir un nouveau commit à l'extrémité de la branche. C'est ce qui cause l'erreur non transférable.
Oui. Et vous pouvez le voir dans la sortie de
git branch
:ahead 1
est ce à quoi vous vous attendez, maisbehind 1
vous indique clairement que vous avez créé des branches divergentes.