Question Déplacez les plus récents commit (s) vers une nouvelle branche avec Git


J'aimerais déplacer les derniers plusieurs commits que j'ai promis de maîtriser à une nouvelle branche et ramener maître à avant que ces commits aient été faits. Malheureusement, mon Git-fu n'est pas encore assez fort, aucune aide?

C'est à dire. Comment puis-je partir de cette

master A - B - C - D - E

pour ça?

newbranch     C - D - E
             /
master A - B 

3753
2017-10-27 03:07


origine


Réponses:


Passer à une nouvelle succursale

ATTENTION: Cette méthode fonctionne parce que vous créez une nouvelle branche avec la première commande: git branch newbranch. Si vous voulez déplacer des commits vers un branche existante vous devez fusionner vos modifications dans la branche existante avant d'exécuter git reset --hard HEAD~3 (voir Déplacement vers une succursale existante au dessous de). Si vous ne fusionnez pas vos modifications en premier, elles seront perdues.

À moins qu'il y ait d'autres circonstances impliquées, ceci peut être facilement fait en ramifiant et en reculant.

# Note: Any changes not committed will be lost.
git branch newbranch      # Create a new branch, saving the desired commits
git reset --hard HEAD~3   # Move master back by 3 commits (GONE from master)
git checkout newbranch    # Go to the new branch that still has the desired commits

Mais assurez-vous combien s'engage à revenir en arrière. Alternativement, vous pouvez au lieu de HEAD~3, fournissez simplement le hash du commit (ou la référence origine / maître) vous voulez "revenir à" sur le maîtriser (/ current) branche, par exemple:

git reset --hard a1b2c3d4

* 1 Vous allez seulement être "perdant" commet de la branche maîtresse, mais ne vous inquiétez pas, vous aurez ces commits dans newbranch!

ATTENTION: Avec Git version 2.0 et plus tard, si vous plus tard git rebase la nouvelle branche sur l'original (master), vous pouvez avoir besoin d'un --no-fork-point option pendant le rebasage pour éviter de perdre les commits reportés. Ayant branch.autosetuprebase always ensemble rend cela plus probable. Voir La réponse de John Mellor pour plus de détails.

Déplacement vers une succursale existante

Si vous souhaitez déplacer vos validations vers un branche existante, Il ressemblera à ceci:

git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch

4778
2017-07-22 22:37



Pour ceux qui se demandent pourquoi cela fonctionne (comme j'étais au début):

Vous voulez revenir en C et déplacer D et E vers la nouvelle branche. Voici à quoi ça ressemble au début:

A-B-C-D-E (HEAD)
        ↑
      master

Après git branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

Après git reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

Comme une branche n'est qu'un pointeur, maîtriser pointé vers le dernier commit. Quand vous avez fait nouvelle branche, vous avez simplement créé un nouveau pointeur sur le dernier commit. Puis en utilisant git reset vous avez déplacé le maîtriser pointeur arrière deux commits. Mais puisque tu n'as pas bougé nouvelle branche, il pointe toujours vers le commit qu'il a fait à l'origine.


846
2018-02-07 16:58



En général...

La méthode exposée par sykora est la meilleure option dans ce cas. Mais parfois ce n'est pas le plus facile et ce n'est pas une méthode générale. Pour une utilisation générale de la méthode Git cerise:

Pour réaliser ce que l'OP veut, c'est un processus en deux étapes:

Étape 1 - Note qui engage du maître que vous voulez sur un newbranch

Exécuter

git checkout master
git log

Notez les hachages de (disons 3) commits que vous voulez sur newbranch. Ici, je vais utiliser:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3 

Remarque: Vous pouvez utiliser les sept premiers caractères ou   tout le hash de commit

Étape 2 - Mettez-les sur le newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

OU (sur Git 1.7.2+, utilisez les plages)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

Git cerise applique ces trois engagements à newbranch.


342
2018-03-26 08:13



Encore une autre façon de le faire, en utilisant seulement 2 commandes. Maintient également votre arbre de travail actuel intact.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

Ancienne version - avant que j'apprenne git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

Être capable de push à . C'est un bon truc à savoir.


251
2018-04-06 22:38



La plupart des réponses précédentes sont dangereusement fausses!

Ne faites pas cela:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

Comme la prochaine fois que vous courez git rebase (ou git pull --rebase) ces 3 commits seraient rejetés silencieusement de newbranch! (voir l'explication ci-dessous)

Au lieu de cela, faites ceci:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • Tout d'abord, il rejette les 3 derniers commits (--keep est comme --hard, mais plus sûr, car échoue plutôt que de jeter les changements non validés).
  • Puis il bifurque newbranch.
  • Ensuite, il sélectionne ces 3 commits de nouveau sur newbranch. Comme ils ne sont plus référencés par une branche, ils le font en utilisant git's reflog: HEAD@{2} est le commit que HEAD utilisé pour se référer à 2 opérations il y a, c'est-à-dire avant que nous 1. vérifié newbranch et 2. utilisé git reset rejeter les 3 commits.

Attention: le refog est activé par défaut, mais si vous l'avez désactivé manuellement (par exemple en utilisant un dépôt git "bare"), vous ne pourrez pas récupérer les 3 validations après l'exécution. git reset --keep HEAD~3.

Une alternative qui ne repose pas sur le refog est:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(Si vous préférez, vous pouvez écrire @{-1} - la branche précédemment vérifiée - au lieu de oldbranch).


Explication technique

Pourquoi serait git rebase rejeter les 3 commits après le premier exemple? C'est parce que git rebase sans arguments permet la --fork-point option par défaut, qui utilise le refog local pour essayer d'être robuste contre la branche en amont qui est poussée par la force.

Supposons que vous ayez dérivé origin / master quand il contenait les commits M1, M2, M3, puis vous avez fait trois commits:

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

mais alors quelqu'un réécrit l'histoire en forçant l'origine / maître pour supprimer M2:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

En utilisant votre reflog local, git rebase peut voir que vous avez bifurqué à partir d'une incarnation antérieure de la branche origin / master, et par conséquent que les commits M2 et M3 ne font pas vraiment partie de votre branche de sujet. Par conséquent, il est raisonnable de supposer que puisque M2 a été supprimé de la branche amont, vous ne le souhaitez plus dans votre branche de sujet une fois que la branche de sujet est rebasée:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

Ce comportement a du sens, et est généralement la bonne chose à faire lors du rebasage.

Donc, la raison pour laquelle les commandes suivantes échouent:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

C'est parce qu'ils laissent le reflog dans le mauvais état. Git voit newbranch comme ayant bifurqué de la branche en amont à une révision qui comprend les 3 commits, puis le reset --hard réécrit l'historique de l'amont pour supprimer les commits, et ainsi la prochaine fois que vous courez git rebase il les rejette comme tout autre commit qui a été retiré de l'amont.

Mais dans ce cas particulier, nous voulons que ces trois engagements soient considérés comme faisant partie de la branche thématique. Pour y parvenir, nous devons déboiter l'amont à la révision précédente qui n'inclut pas les 3 commits. C'est ce que font mes solutions suggérées, donc ils laissent tous deux le réflecteur dans l'état correct.

Pour plus de détails, voir la définition de --fork-point dans le git rebase et git merge-basedocs.


209
2017-10-19 14:12



Cela ne les "déplace" pas dans le sens technique mais cela a le même effet:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)

27
2018-01-21 16:10



Pour ce faire, sans réécrire l'historique (c'est-à-dire si vous avez déjà activé les validations):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Les deux branches peuvent alors être poussées sans forcer!


19
2017-09-20 10:17



Avait juste cette situation:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

J'ai joué:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

Je m'attendais à ce que je sois le chef, mais commettez le L est maintenant ...

Pour être sûr d'atterrir au bon endroit dans l'histoire, il est plus facile de travailler avec le hash du commit

git branch newbranch 
git reset --hard #########
git checkout newbranch

12
2018-05-01 07:50



Une solution beaucoup plus simple

Si:

  1. Votre but principal est de revenir en arrière master, et
  2. Vous voulez garder les changements mais ne vous souciez pas particulièrement du commits individuels, et
  3. Tu n'as pas encore poussé,

Alors ce qui suit est beaucoup plus simple:

git reset HEAD~3
git stash
git checkout otherbranch
git stash pop

Qu'est-ce que cela fait:

  1. Annule les trois derniers commits (et leurs messages), mais laisse tous les fichiers de travail intacts
  2. Stashes tous les changements de fichiers de travail, rendant le master arbre de travail égal à l'état HEAD ~ 3.
  3. Passer à une branche existante otherbranch
  4. Extrait les fichiers modifiés de la cachette et dans votre répertoire de travail.

Vous pouvez maintenant utiliser git add et git commit comme vous le feriez normalement. Tous les changements seront engagés dans otherbranch.

Cela ne préserve pas les commits erronés et ne commet pas de messages. Vous devrez ajouter un nouveau message de validation à cette nouvelle validation, en recréant vos messages de validation antérieurs. Cependant, le PO a déclaré que l'objectif était de «revenir maître avant que ces commits aient été faits» sans perdre des changements, et cette solution fait cela.

Je fais cela au moins une fois par semaine quand je commet accidentellement de nouveaux master au lieu de develop. Habituellement, je n'ai qu'un engagement à annuler, auquel cas git reset HEAD^ fonctionne pour restaurer un seul commit.


6
2018-05-25 14:14



Vous pouvez le faire est juste 3 étapes simples que j'ai utilisées.

1) Créer une nouvelle branche où vous voulez valider votre mise à jour récente.

git branch <branch name> 

2) Trouver l'identifiant de validation récent pour la validation sur une nouvelle branche.

git log

3) Copiez cette note d'identification de commit que la liste de commit la plus récente a lieu en haut. afin que vous puissiez trouver votre engagement. Vous trouverez également ceci via un message.

git cherry-pick d34bcef232f6c... 

vous pouvez également fournir un identifiant de validation.

git cherry-pick d34bcef...86d2aec

Maintenant, votre travail est terminé. Si vous avez choisi l'id correct et la branche correcte alors vous réussirez. Alors avant, faites attention. Un autre problème peut survenir.

Maintenant vous pouvez pousser votre code

git push


0
2018-06-01 05:40



1) Créez une nouvelle branche, qui déplace toutes vos modifications vers new_branch.

git checkout -b new_branch

2) Puis retournez à la vieille branche.

git checkout master

3) Faire rebaser git

git rebase -i <short-hash-of-B-commit>

4) Ensuite, l'éditeur ouvert contient les 3 dernières informations de validation.

...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...

5) Changer pick à drop dans tous ces 3 commits. Ensuite, enregistrez et fermez l'éditeur.

...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...

6) Maintenant les 3 derniers commits sont supprimés de la branche courante (master). Maintenant pousser la branche avec force, avec + signer avant le nom de la branche.

git push origin +master

0