256 votes

Définir le pointeur parent git sur un parent différent

Si j’ai un commit dans le passé qui pointe vers un parent mais que je veux changer de parent, c’est ce que je ferais pour le faire.

452voto

Amber Points 159296

À l'aide de git rebase. C'est le générique de "prendre commit(s) et plop il/sur un autre parent (de base) de la commande" dans Git.

Quelques choses à savoir, cependant:

  1. Depuis commettre SHAs impliquer leurs parents, lorsque vous modifiez le parent d'un commit, ses SHA va changer - de même que le SHAs de tous les commits qui viennent après lui (plus récent) dans la ligne de développement.

  2. Si vous travaillez avec d'autres personnes, et vous avez déjà poussé le commit en question du public à l'endroit où ils ont tiré en modifiant le commit est probablement une Mauvaise Idée™. Cela est dû à #1, et donc la confusion qui en résulte les autres utilisateurs référentiels de rencontre en essayant de comprendre ce qui s'est passé en raison de votre SHAs n'a plus de correspondance de leur vie pour la "même" s'engage. (Voir la "RÉCUPÉRATION DE l'AMONT REBASE" section de la page de manuel pour plus de détails.)

Cela dit, si vous êtes actuellement sur une branche avec certains s'engage à ce que vous souhaitez déplacer vers un nouveau parent, ça ressemble à quelque chose comme ceci:

git rebase --onto <new-parent> <old-parent>

Qui va passer le tout après l' <old-parent> dans la branche à s'asseoir sur le dessus de l' <new-parent> à la place.

35voto

Chris Johnsen Points 50064

Si il s'avère que vous avez besoin pour éviter la relocalisation de la subséquente s'engage (par exemple à cause d'une histoire de réécriture serait un comble), alors vous pouvez utiliser la commande git remplacer (disponible dans Git 1.6.5 et plus tard).

# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.

replace_first_parent() {
    old_parent=$(git rev-parse --verify "${1}^1") || return 1
    new_parent=$(git rev-parse --verify "${2}^0") || return 2
    new_commit=$(
      git cat-file commit "$1" |
      sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
      git hash-object -t commit -w --stdin
    ) || return 3
    git replace "$1" "$new_commit"
}
replace_first_parent B A

# …---o---A---o---o---…
#          \
#           C---b---b---…
#
# C is the replacement for B.

Avec le au-dessus de remplacement établie, les demandes de l'objet B est en fait le retour de l'objet C. Le contenu de C sont exactement les mêmes que le contenu de B, sauf pour le premier parent (même les parents (sauf pour le premier), de même arbre, le même message de commit).

Les remplacements sont actives par défaut, mais peuvent être activés à l'aide de la --no-replace-objects option de git (avant le nom de la commande) ou par la définition de la GIT_NO_REPLACE_OBJECTS variable d'environnement. Les remplacements peuvent être partagés en poussant refs/replace/* (en plus de la normale refs/heads/*) .

Si vous n'aimez pas la commettre-munging (avec sed ci-dessus), vous pouvez créer votre remplacement valider à l'aide de niveau supérieur commandes:

git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -

La grande différence est que cette séquence ne pas propager les nouveaux parents si B est une fusion de commettre.

34voto

Jakub Narębski Points 87537

Notez que la modification d'un commit dans Git exige que tous les commits qui le suivent alse être changé. Ceci est déconseillé si vous avez publié cette partie de l'histoire, et quelqu'un peut avoir à construire leurs travaux sur l'histoire qu'il était avant le changement.

Solution de rechange git rebase mentionné dans l'Ambre de la réponse est d'utiliser des greffes de mécanisme (voir la définition de Git greffes dans Git Glossaire et de la documentation de l' .git/info/grafts le fichier dans le Dépôt Git de Mise en page de la documentation) pour changer parent de s'engager, vérifiez qu'il n'chose correcte avec un peu d'histoire de la visionneuse (gitk, git log --graph, etc.) et puis utiliser git filter-branch (comme décrit dans la section "Exemples" de sa page de man) à la rendre permanente (et puis l'enlever de la greffe, et éventuellement supprimer l'original refs soutenu par git filter-branch, ou reclone référentiel):

echo "$commit-id $greffon-id" >> .git/info/greffes
git filter-branch $greffon-id de la TÊTE..

NOTE!!!!! Cette solution est différente de rebase solution dans cette git rebase serait rebase / greffe de changements, alors que les greffes de base de la solution serait tout simplement de reparent s'engage comme il est, ne prenant pas en compte les différences entre les vieux parents et des nouveaux parents!

26voto

Mark Lodato Points 6548

Pour clarifier les réponses ci-dessus, et sans vergogne fiche de mon propre script:

Cela dépend si vous voulez "rebase" ou "reparent". Un rebase, comme suggéré par l'Ambre, se déplace autour de diff. Un reparent, comme suggéré par Jakub et Chris, se déplace autour de snaphots. Si vous voulez reparent, je vous suggère d'utiliser git reparent plutôt que de faire le travail manuellement.

Comparaison

Supposons que vous avez sur la photo de gauche et vous voulez qu'il ressemble sur la photo de droite:

                   C'
                  /
A---B---C        A---B---C

À la fois complet et reparenting produira le même tableau, mais la définition de l' C' diffère. Avec git rebase --onto A B, C' ne contiennent pas toutes les modifications introduites par B. Avec git reparent -p A, C' seront identiques à C (sauf qu' B ne seront pas dans l'histoire).

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