web-dev-qa-db-fra.com

commande git pour faire une branche comme une autre

J'essaie de prendre une branche avec des modifications et de la ramener pour qu'elle soit identique à celle en amont à partir de laquelle elle a divergé. Les modifications sont à la fois locales et ont été poussées vers github, donc ni git reset ni git rebase ne sont vraiment viables, car elles modifient l’historique, ce qui est une mauvaise chose pour une branche déjà poussée.

J'ai également essayé git merge avec diverses stratégies, mais aucune d'entre elles n'annule les modifications locales, c'est-à-dire que si j'avais ajouté un fichier, une fusion pourrait ramener d'autres fichiers en ligne, mais j'aurai toujours ce fichier que l'amont n'en contient pas t ont.

Je pourrais simplement créer une nouvelle branche en amont, mais j'aimerais vraiment une fusion qui, en termes d'historique des révisions, applique tous les changements pour que ma branche redevienne identique à l'amont, afin de pouvoir en toute sécurité pousser ce changement sans brouiller l'histoire. Existe-t-il une telle commande ou série de commandes?

67
Arne Claassen

Vous pouvez fusionner votre branche en amont avec votre branche dev avec un pilote de fusion personnalisé "keepTheirs":
Voir " " git merge -s theirs "nécessaire - mais je sais que cela n'existe pas ".
Dans votre cas, un seul .gitattributes serait requis et un script keepTheirs du type:

mv -f $3 $2
exit 0

git merge --strategy=theirs Simulation n ° 1

S'affiche en tant que fusion, avec le premier parent en amont.

Jefromi mentionne (dans les commentaires) le merge -s ours en fusionnant votre travail sur l'amont (ou sur une branche temporaire à partir de l'amont), puis en effectuant une avance rapide de votre branche vers le résultat de cette fusion:

git checkout -b tmp Origin/upstream
git merge -s ours downstream         # ignoring all changes from downstream
git checkout downstream
git merge tmp                        # fast-forward to tmp HEAD
git branch -D tmp                    # deleting tmp

Cela présente l'avantage d'enregistrer l'ancêtre en amont en tant que premier parent, de sorte que la fusion signifie "absorbe cette branche de sujet obsolète" plutôt que "détruit cette branche de sujet et la remplace par celle en amont".

(Edit 2011):

Ce workflow a été rapporté dans ce blog par l'OP :

Pourquoi est-ce que je le veux encore?

Tant que mon rapport n'avait rien à voir avec la version publique, tout allait bien, mais comme je souhaiterais maintenant pouvoir collaborer avec d'autres membres de l'équipe et des contributeurs externes sur le WIP, je veux m'assurer que mes succursales publiques sont bien connectées. fiable pour les autres, c’est-à-dire qu’il n’ya plus de rebase et de réinitialisation des éléments que j’ai poussés vers la sauvegarde à distance, car ils sont maintenant sur GitHub et sur public.

Cela me laisse donc comment procéder.
99% du temps, ma copie ira dans le maître en amont, donc je veux travailler mon maître et pousser en amont la plupart du temps.
Mais de temps en temps, ce que j'ai dans wip sera invalidé par ce qui se passe en amont et j'abandonnerai une partie de mon wip.
À ce stade, je souhaite rétablir la synchronisation de mon maître avec l'amont, sans détruire aucun point de validation sur mon maître publiquement poussé. Par exemple, je souhaite une fusion avec l'amont qui aboutit au groupe de modifications qui rend ma copie identique à l'amont..
Et c'est ce que git merge --strategy=theirs devrait faire.


git merge --strategy=theirs Simulation n ° 2

_ {Apparaît en tant que fusion, avec le nôtre en tant que premier parent.

(proposé par jcwenger )

git checkout -b tmp upstream
git merge -s ours thebranch         # ignoring all changes from downstream
git checkout downstream
git merge --squash tmp               # apply changes from tmp but not as merge.
git rev-parse upstream > .git/MERGE_HEAD #record upstream 2nd merge head
git commit -m "rebaselined thebranch from upstream" # make the commit.
git branch -D tmp                    # deleting tmp

git merge --strategy=theirs Simulation n ° 3

Ce article de blog mentionne :

git merge -s ours ref-to-be-merged
git diff --binary ref-to-be-merged | git apply -R --index
git commit -F .git/COMMIT_EDITMSG --amend

parfois, vous voulez faire cela, et non pas parce que votre historique contient des "merdes", mais bien {peut-être parce que vous voulez changer la base de développement pour un référentiel public où le rebasement doit être évité}.


git merge --strategy=theirs Simulation n ° 4

(même article de blog)

Si vous souhaitez que les branches en amont locales puissent être expédiées rapidement, un compromis pourrait consister à travailler en sachant que pour sid/unstable, la branche en amont peut de temps à autre être réinitialisée/rebasée (en fonction d'événements ultérieurs de votre contrôle du côté du projet en amont).
Ce n'est pas un gros problème et travailler avec cette hypothèse signifie qu'il est facile de garder la branche amont en local dans un état où il ne faut que des mises à jour rapides.

git branch -m upstream-unstable upstream-unstable-save
git branch upstream-unstable upstream-remote/master
git merge -s ours upstream-unstable
git diff --binary ref-to-be-merged | git apply -R --index --exclude="debian/*"
git commit -F .git/COMMIT_EDITMSG --amend

git merge --strategy=theirs Simulation # 5

(proposé par Barak A. Pearlmutter ):

git checkout MINE
git merge --no-commit -s ours HERS
git rm -rf .
git checkout HERS -- .
git checkout MINE -- debian # or whatever, as appropriate
git gui # edit commit message & click commit button

git merge --strategy=theirs Simulation # 6

(proposé par le même Michael Gebetsroither ):

Michael Gebetsroither intervint, affirmant que je "trompais";) et donna une autre solution avec des commandes de plomberie de niveau inférieur:

(ça ne serait pas git si ce ne serait pas possible avec seulement des commandes git, tout avec git avec diff/patch/apply n'est pas une vraie solution;).

# get the contents of another branch
git read-tree -u --reset <ID>
# selectivly merge subdirectories
# e.g superseed upstream source with that from another branch
git merge -s ours --no-commit other_upstream
git read-tree --reset -u other_upstream     # or use --prefix=foo/
git checkout HEAD -- debian/
git checkout HEAD -- .gitignore
git commit -m 'superseed upstream source' -a
89
VonC

Vous pouvez le faire assez facilement maintenant:

$ git fetch Origin
$ git merge Origin/master -s recursive -Xtheirs

Cela synchronise votre référentiel local avec l'Origin et préserve l'historique.

13
user1828582

Il me semble que vous devez juste faire:

$ git reset --hard Origin/master

S'il n'y a pas de changement dans Push upstream et que vous voulez simplement que la branche en amont soit votre branche actuelle, cela se fera. Ce n'est pas dangereux de faire cela localement mais vous perdrez toutes les modifications locales ** qui n'ont pas été forcées à maîtriser.

** En fait, les modifications sont toujours d'actualité si vous les avez validées localement, car les validations seront toujours dans votre git reflog, généralement pendant au moins 30 jours.

13
wuputah

Une autre simulation pour git merge -s theirs ref-to-be-merged:

git merge --no-ff -s ours ref-to-be-merged         # enforce a merge commit; content is still wrong
git reset --hard HEAD^2; git reset --soft HEAD@{1} # fix the content
git commit --amend

Une alternative à la double réinitialisation serait l’application du patch inverse:

git diff --binary ref-to-be-merged | git apply -R --index
5
michas

Il y a aussi un moyen avec peu d'aide de commande de plomberie - IMHO le plus simple. Supposons que vous souhaitiez émuler "le leur" pour le cas 2 branches:

head1=$(git show --pretty=format:"%H" -s foo)
head2=$(git show --pretty=format:"%H" -s bar)
tree=$(git show --pretty=format:"%T" -s bar)
newhead=$(git commit-tree $tree -p $head1 -p $head2 <<<"merge commit message")
git reset --hard $newhead

Ceci fusionne un nombre arbitraire de têtes (2 dans l'exemple ci-dessus) en utilisant l'arborescence de l'une d'entre elles (barre dans l'exemple ci-dessus, fournissant l'arborescence 'leur'), ​​sans tenir compte des problèmes de diff/fichier ne se soucie pas de ceux-ci). Notez que la tête peut être juste 1 (donc l'équivalent de cherry-pick avec "le leur").

Notez que la tête parent spécifiée en premier peut influencer certains éléments (voir, par exemple, --first-parent de la commande git-log) - par conséquent, gardez cela à l'esprit.

Au lieu de git-show, tout ce qui est capable de produire des hachages d'arbre et de commit peut être utilisé - quel que soit le type utilisé pour analyser (fichier-cat, liste-de-rev, ...). Vous pouvez tout suivre avec git commit --amend pour embellir de manière interactive le message de commit.

3
Michal Soltys

Utilisez git reset BACKWARDS!

Vous pouvez faire en sorte qu'une branche ressemble à n'importe quel autre commit avec git reset, mais vous devez le faire de manière approximative.

Pour qu'une branche sur commit <old> ressemble à un commit <new>, vous pouvez le faire

git reset --hard <new>

afin de rendre <new> le contenu de l’arbre de travail.

Alors fais 

git reset --mixed <old> 

pour rétablir la branche dans son commit original mais en laissant l’arbre de travail dans <new> state .

Ensuite, vous pouvez ajouter et valider les modifications, afin que votre branche corresponde exactement au contenu de la validation <new>.

Il est contre-intuitif que pour passer de l'état <old> au <new>, vous devez créer un git reset de <new> à <old>. Cependant, avec l'option --mixed, l'arbre de travail reste à <new> et le pointeur de branche défini sur <old>. Ainsi, lorsque les modifications sont validées, la branche a l'apparence souhaitée.

Attention

Ne perdez pas la trace de vos commits, par exemple oublier ce que <old> est en faisant git reset --hard <new>.

1
user3070485

Les mains lourdes, mais bon sang, qu'est-ce qui peut mal tourner?

  • Découvrez la branche X à laquelle vous voulez ressembler le Y
  • cp -r .git/tmp
  • Départ branche Y
  • rm -rf .git && cp -r /tmp/.git.
  • Commit & Push any difference
  • TERMINÉ.
1
sscarduzio

passez à la branche en amont distante et créez un git merge avec la stratégie de fusion définie sur ours.

git checkout Origin/master
git merge dev --strategy=ours
git commit ...
git Push

Toute l'histoire sera toujours présente, mais vous aurez un commit de fusion supplémentaire. La chose importante ici est de commencer à partir de la version que vous voulez et de fusionner ours avec la branche github.

1
kelloti

J'ai suivi ces rôles:

Récupération de l’origine, réinitialisation matérielle de la branche puis récursive de la leur puis forcée Push vers la branche

A VOTRE PROPRE RISQUE

git fetch Origin
git reset --hard Origin/<branch>
git merge Origin/<branch> -s recursive -Xtheirs
git Push -f <remote> <branch>
0
esso