J'ai déjà demandé comment écraser les deux premiers commits dans un référentiel git.
Bien que les solutions soient plutôt intéressantes et pas vraiment aussi déformantes que d'autres choses dans git, elles sont toujours un peu le sac proverbial de la douleur si vous devez répéter la procédure plusieurs fois pendant le développement de votre projet.
Donc, je préfère ne souffrir qu'une seule fois, puis être en mesure d'utiliser à jamais le rebase interactif standard.
Ce que je veux faire, alors, c'est d'avoir un commit initial vide qui existe uniquement dans le but d'être le premier. Pas de code, rien du tout. Il suffit de prendre de l'espace pour qu'il puisse être la base du rebase.
Ma question est alors, ayant un référentiel existant, comment puis-je insérer un nouveau commit vide avant le premier et déplacer tout le monde vers l'avant?
Réponses:
Il y a 2 étapes pour y parvenir:
Nous allons mettre le nouveau commit vide sur une branche temporaire
newroot
pour plus de commodité.1. Créez un nouveau commit vide
Il existe plusieurs façons de procéder.
Utiliser uniquement de la plomberie
L'approche la plus propre consiste à utiliser la plomberie de Git pour simplement créer un commit directement, ce qui évite de toucher la copie de travail ou l'index ou la branche qui est extraite, etc.
Créez un objet arborescent pour un répertoire vide:
Enveloppez un commit autour de lui:
Créez-y une référence:
Vous pouvez bien sûr réorganiser toute la procédure en une seule ligne si vous connaissez bien votre coque.
Sans plomberie
Avec les commandes porcelaine classiques, vous ne pouvez pas créer un commit vide sans vérifier la
newroot
branche et mettre à jour l'index et la copie de travail à plusieurs reprises, sans raison valable. Mais certains peuvent trouver cela plus facile à comprendre:Notez que sur les très anciennes versions de Git qui n'ont pas le
--orphan
commutateurcheckout
, vous devez remplacer la première ligne par ceci:2. Réécrire l'historique pour commencer à partir de ce commit vide
Vous avez deux options ici: le rebasage ou une réécriture de l'historique propre.
Rebasing
Cela a la vertu de la simplicité. Cependant, il mettra également à jour le nom et la date du committer à chaque dernier commit sur la branche.
En outre, avec certains historiques de cas de bord, il peut même échouer en raison de conflits de fusion - malgré le fait que vous rebasiez sur une validation qui ne contient rien.
Réécriture de l'histoire
L'approche la plus propre consiste à réécrire la branche. Contrairement à with
git rebase
, vous devrez rechercher à partir de quel commit votre branche commence:La réécriture se produit dans la deuxième étape, évidemment; c'est la première étape qui a besoin d'explications. Qu'est
git replace
- ce que c'est, il dit à Git que chaque fois qu'il voit une référence à un objet que vous souhaitez remplacer, Git devrait plutôt regarder le remplacement de cet objet.Avec le
--graft
commutateur, vous lui dites quelque chose de légèrement différent de la normale. Vous dites que vous n'avez pas encore d'objet de remplacement, mais vous souhaitez remplacer l'<currentroot>
objet de validation par une copie exacte de lui-même, à l' exception que les validations parentes du remplacement doivent être celles que vous avez répertoriées (c'est-à-dire lanewroot
validation ). Puisgit replace
va de l'avant et crée ce commit pour vous, puis déclare ce commit comme remplacement de votre commit d'origine.Maintenant, si vous faites un
git log
, vous verrez que les choses ressemblent déjà à ce que vous voulez: la branche démarrenewroot
.Cependant, notez que
git replace
cela ne modifie pas réellement l'historique - ni ne se propage hors de votre référentiel. Il ajoute simplement une redirection locale vers votre référentiel d'un objet à un autre. Cela signifie que personne d'autre ne voit l'effet de ce remplacement - seulement vous.C'est pourquoi l'
filter-branch
étape est nécessaire. Avecgit replace
vous créez une copie exacte avec des validations parent ajustées pour la validation racine;git filter-branch
répète ensuite ce processus pour toutes les validations suivantes également. C'est là que l'histoire est réellement réécrite pour que vous puissiez la partager.la source
--onto newroot
option est redondante; vous pouvez vous en passer car l'argument que vous lui transmetteznewroot
est le même que l'argument en amont -newroot
.git-checkout
ce changement. Je l'ai mis à jour pour mentionner cette approche en premier, merci pour le pointeur.git rebase --merge -s recursive -X theirs --onto newroot --root master
pour résoudre automatiquement tous les conflits (voir cette réponse). @AlexanderKuzinFusion des réponses d'Aristote Pagaltzis et Uwe Kleine-König et du commentaire de Richard Bronosky.
(juste pour tout mettre au même endroit)
la source
git rebase newroot master
raison d'une erreur.J'aime la réponse d'Aristote. Mais j'ai constaté que pour un grand référentiel (> 5000 validations), la branche de filtre fonctionne mieux que rebaser pour plusieurs raisons 1) elle est plus rapide 2) elle ne nécessite pas d'intervention humaine en cas de conflit de fusion. 3) il peut réécrire les balises - en les préservant. Notez que filter-branch fonctionne car il n'y a pas de question sur le contenu de chaque commit - c'est exactement la même chose qu'avant ce 'rebase'.
Mes étapes sont:
Notez que les options '--tag-name-filter cat' signifient que les balises seront réécrites pour pointer vers les commits nouvellement créés.
la source
J'ai utilisé avec succès des morceaux de la réponse d'Aristote et de Kent:
Cela réécrira également toutes les branches (pas seulement
master
) en plus des balises.la source
refs/original/
et supprime chaque référence. Les références qu'il supprime devraient déjà être référencées par une autre branche, afin qu'elles ne disparaissent pas vraiment, soient simplementrefs/original/
supprimées.timedatectl set-time '2017-01-01 00:00:00'
donnaisnewroot
un ancien horodatage.git rebase --root --onto $emptyrootcommit
devrait faire l'affaire facilement
la source
$emptyrootcommit
est une variable shell qui se développe à rien, sûrement?Je pense qu'utiliser
git replace
etgit filter-branch
est une meilleure solution que d'utiliser ungit rebase
:L'idée derrière cela est de:
git filter-branch
Voici un script pour les 2 premières étapes:
Vous pouvez exécuter ce script sans risque (même si faire une sauvegarde avant de faire une action que vous n'avez jamais faite auparavant est une bonne idée;)), et si le résultat n'est pas celui attendu, supprimez simplement les fichiers créés dans le dossier
.git/refs/replace
et réessayez; )Une fois que vous avez vérifié que l'état du référentiel correspond à vos attentes, exécutez la commande suivante pour mettre à jour l'historique de toutes les branches :
Maintenant, vous devez voir 2 historiques, l'ancien et le nouveau (voir l'aide sur
filter-branch
pour plus d'informations). Vous pouvez comparer les 2 et vérifier à nouveau si tout va bien. Si vous êtes satisfait, supprimez les fichiers dont vous n'avez plus besoin:Vous pouvez retourner dans votre
master
branche et supprimer la branche temporaire:Maintenant, tout doit être fait;)
la source
Je me suis excité et j'ai écrit une version «idempotente» de ce joli script ... il insérera toujours le même commit vide, et si vous l'exécutez deux fois, cela ne change pas vos hachages de commit à chaque fois. Alors, voici mon point de vue sur git-insert-empty-root :
Vaut-il la complexité supplémentaire? peut-être pas, mais je vais utiliser celui-ci.
Cela DEVRAIT également permettre d'effectuer cette opération sur plusieurs copies clonées du dépôt, et se retrouver avec les mêmes résultats, donc ils sont toujours compatibles ... test ... oui, ça marche, mais il faut aussi supprimer et ajouter votre télécommandes à nouveau, par exemple:
la source
Pour ajouter un commit vide au début d'un référentiel, si vous avez oublié de créer un commit vide immédiatement après "git init":
la source
Eh bien, voici ce que j'ai trouvé:
la source
Voici mon
bash
script basé sur la réponse de Kent avec des améliorations:master
, une fois terminé;git checkout --orphan
ne fonctionne qu'avec une branche, pas avec un état de tête détachée, elle est donc extraite suffisamment longtemps pour valider la nouvelle racine, puis supprimée;filter-branch
(Kent a laissé un espace réservé là pour le remplacement manuel);filter-branch
opération ne réécrit que les succursales locales, pas trop les télécommandesla source
Pour changer le commit racine:
Créez d'abord le commit que vous souhaitez comme premier.
Ensuite, changez l'ordre des validations en utilisant:
git rebase -i --root
Un éditeur apparaîtra avec les validations jusqu'à la validation racine, comme:
choisir 1234 ancien message racine
pick 0294 Un commit au milieu
choisissez 5678 commit que vous voulez mettre à la racine
Vous pouvez ensuite mettre le commit que vous voulez en premier, en le plaçant dans la première ligne. Dans l'exemple:
choisissez 5678 commit que vous voulez mettre à la racine
choisir 1234 ancien message racine
pick 0294 Un commit au milieu
Quittez l'éditeur, l'ordre de validation aura changé.
PS: Pour modifier l'éditeur git utilise, exécutez:
git config --global core.editor name_of_the_editor_program_you_want_to_use
la source
Combinant le dernier et le plus grand. Pas d'effets secondaires, pas de conflits, garder les tags.
Notez que sur GitHub, vous perdrez les données d'exécution de CI et PR pourrait être gâché à moins que d'autres branches ne soient également corrigées.
la source
Réponse suivante Aristote Pagaltzis et autres mais en utilisant des commandes plus simples
Notez que votre dépôt ne doit contenir aucune modification locale en attente de validation.
Remarque
git checkout --orphan
fonctionnera dans les nouvelles versions de git, je suppose.Notez que la plupart du temps
git status
donne des conseils utiles.la source
Démarrez un nouveau référentiel.
Réglez votre date à la date de début que vous souhaitez.
Faites tout comme vous le souhaiteriez, en ajustant l'heure du système pour refléter le moment où vous auriez souhaité que vous le fassiez de cette façon. Tirez les fichiers du référentiel existant selon vos besoins pour éviter de trop taper inutilement.
Lorsque vous arrivez à aujourd'hui, échangez les référentiels et vous avez terminé.
Si vous êtes juste fou (établi) mais raisonnablement intelligent (probablement, parce que vous devez avoir une certaine quantité d'intelligence pour imaginer des idées folles comme celle-ci), vous scripterez le processus.
Cela sera également plus agréable lorsque vous déciderez que le passé s'est produit d'une autre manière dans une semaine.
la source
Je sais que ce message est ancien, mais cette page est la première lorsque Google recherche "insérer un git de validation".
Pourquoi compliquer les choses simples?
Vous avez ABC et vous voulez ABZC.
git rebase -i trunk
(ou n'importe quoi avant B)git add ..
git commit
(git commit --amend
qui modifiera B et ne créera pas Z)[Vous pouvez en faire autant
git commit
que vous le souhaitez ici pour insérer plus de commits. Bien sûr, vous pouvez avoir des problèmes avec l'étape 5, mais résoudre les conflits de fusion avec git est une compétence que vous devriez avoir. Sinon, entraînez-vous!]git rebase --continue
C'est simple, non?
Si vous comprenez
git rebase
, l'ajout d'un commit 'root' ne devrait pas être un problème.Amusez-vous avec git!
la source
git rebase
ne peut pas faire ça.