Je viens de lire la modification d'un seul fichier dans un commit passé dans git mais malheureusement la solution acceptée «réorganise» les commits, ce qui n'est pas ce que je veux. Alors voici ma question:
De temps en temps, je remarque un bogue dans mon code en travaillant sur une fonctionnalité (non liée). Un rapide git blame
révèle ensuite que le bogue a été introduit il y a quelques commits (je commets beaucoup, donc généralement ce n'est pas le commit le plus récent qui a introduit le bogue). À ce stade, je fais généralement ceci:
git stash # temporarily put my work aside
git rebase -i <bad_commit>~1 # rebase one step before the bad commit
# mark broken commit for editing
vim <affected_sources> # fix the bug
git add <affected_sources> # stage fixes
git commit -C <bad_commit> # commit fixes using same log message as before
git rebase --continue # base all later changes onto this
Cependant, cela arrive si souvent que la séquence ci-dessus devient ennuyeuse. Surtout le «rebase interactif» est ennuyeux. Existe-t-il un raccourci vers la séquence ci-dessus, qui me permet de modifier un commit arbitraire dans le passé avec les modifications par étapes? Je suis parfaitement conscient que cela change l'histoire, mais je fais des erreurs si souvent que j'aimerais vraiment avoir quelque chose comme
vim <affected_sources> # fix bug
git add -p <affected_sources> # Mark my 'fixup' hungs for staging
git fixup <bad_commit> # amend the specified commit with staged changes,
# rebase any successors of bad commit on rewritten
# commit.
Peut-être un script intelligent qui peut réécrire les commits à l'aide d'outils de plomberie ou plus?
rebase -i
?rebase --onto tmp bad-commit master
. Tel qu'il est écrit, il essaiera d'appliquer le mauvais commit à l'état de commit fixe.Réponses:
RÉPONSE MIS À JOUR
Il y a quelque temps, un nouvel
--fixup
argument a été ajouté,git commit
auquel il peut être utilisé pour construire un commit avec un message de journal approprié pourgit rebase --interactive --autosquash
. Donc, le moyen le plus simple de réparer un commit passé est maintenant:RÉPONSE ORIGINALE
Voici un petit script Python que j'ai écrit il y a quelque temps qui implémente cette
git fixup
logique que j'espérais dans ma question initiale. Le script suppose que vous avez organisé certaines modifications, puis que vous les appliquez à la validation donnée.REMARQUE : ce script est spécifique à Windows; il recherche
git.exe
et définit laGIT_EDITOR
variable d'environnement en utilisantset
. Ajustez-le si nécessaire pour les autres systèmes d'exploitation.En utilisant ce script, je peux implémenter précisément le workflow `` réparer les sources cassées, mettre en scène les correctifs, exécuter git fixup '' que j'ai demandé:
la source
git stash
etgit stash pop
autour de votre rebase pour ne plus avoir besoin d'un répertoire de travail propregit stash
etgit stash pop
: vous avez raison, mais malheureusement,git stash
c'est beaucoup plus lent sous Windows que sous Linux ou OS / X. Étant donné que mon répertoire de travail est généralement propre, j'ai omis cette étape pour ne pas ralentir la commande.git rebase -i --fixup
, et cela a rebasé à partir du commit corrigé comme point de départ, donc l'argument sha n'était pas nécessaire dans mon cas.git config --global rebase.autosquash true
Ce que je fais c'est:
Votre éditeur s'ouvrira avec une liste des 5 derniers commits, prêts à être manipulés. Changement:
...à:
Enregistrez et quittez votre éditeur, et le correctif sera réécrit dans le commit auquel il appartient.
Après avoir fait cela plusieurs fois, vous le ferez en quelques secondes pendant votre sommeil. Le rebasage interactif est la fonctionnalité qui m'a vraiment vendu sur git. C'est incroyablement utile pour cela et plus encore ...
la source
--autosquash
commutateur pourgit rebase
, qui réorganise automatiquement les étapes dans l'éditeur pour vous. Voir ma réponse pour un script qui en profite pour implémenter unegit fixup
commande.Un peu tard à la fête, mais voici une solution qui fonctionne comme l'auteur l'imaginait.
Ajoutez ceci à votre .gitconfig:
Exemple d'utilisation:
Cependant, si vous avez des modifications non mises en scène, vous devez les cacher avant le rebase.
Vous pouvez modifier l'alias pour qu'il se cache automatiquement, au lieu de donner un avertissement. Cependant, si la correction ne s'applique pas proprement, vous devrez ouvrir la cachette manuellement après avoir résolu les conflits. Faire à la fois la sauvegarde et le popping manuellement semble plus cohérent et moins déroutant.
la source
git fixup HEAD
j'ai créé un alias. Je pourrais également utiliser amender pour cela, je suppose.amend = commit --amend --reuse-message=HEAD
Ensuite, vous pouvez simplement tapergit amend
ougit amend -a
et ignorer l'éditeur pour le message de validation.Pour réparer un commit:
où a0b1c2d3 est le commit que vous voulez corriger et où 2 est le nombre de commits +1 collés que vous voulez changer.
Remarque: git rebase --autosquash sans -i ne fonctionne pas mais avec -i fonctionne, ce qui est étrange.
la source
--autosquash
sans-i
ne fonctionne toujours pas.EDITOR=true git rebase --autosquash -i
MISE À JOUR: Une version plus propre du script peut maintenant être trouvée ici: https://github.com/deiwin/git-dotfiles/blob/docs/bin/git-fixup .
Je cherchais quelque chose de similaire. Ce script Python semble cependant trop compliqué, c'est pourquoi j'ai mis au point ma propre solution:
Tout d'abord, mes alias git ressemblent à ça (emprunté à ici ):
Maintenant, la fonction bash devient assez simple:
Ce code met d'abord en scène toutes les modifications actuelles (vous pouvez supprimer cette partie si vous souhaitez mettre en scène les fichiers vous-même). Crée ensuite le correctif (squash peut également être utilisé, si c'est ce dont vous avez besoin) commit. Après cela, il lance un rebase interactif avec l'
--autosquash
indicateur sur le parent du commit que vous donnez comme argument. Cela ouvrira votre éditeur de texte configuré, vous pourrez donc vérifier que tout est comme prévu et la simple fermeture de l'éditeur terminera le processus.La
if [[ "$1" == HEAD* ]]
partie (empruntée ici ) est utilisée, car si vous utilisez, par exemple, HEAD ~ 2 comme référence de commit (le commit avec lequel vous voulez corriger les changements actuels), alors le HEAD sera déplacé après la création du commit de correction et vous devrez utiliser HEAD ~ 3 pour faire référence au même commit.la source
Vous pouvez éviter l'étape interactive en utilisant un éditeur "nul":
Cela utilisera
/bin/true
comme éditeur, au lieu de/usr/bin/vim
. Il accepte toujours tout ce que git suggère, sans incitation.la source
call(["set", "GIT_EDITOR=true", "&&", git, "rebase", "-i" ...
).Ce qui m'a vraiment dérangé dans le workflow de correction, c'est que je devais déterminer moi-même dans quel commit je voulais écraser le changement à chaque fois. J'ai créé une commande "git fixup" qui m'aide.
Cette commande crée des commits de correction, avec la magie supplémentaire qu'elle utilise git-deps pour trouver automatiquement le commit pertinent, de sorte que le flux de travail se résume souvent à:
Cela ne fonctionne que si les changements par étapes peuvent être attribués sans ambiguïté à un commit particulier sur l'arbre de travail (entre master et HEAD). Je trouve que c'est le cas très souvent pour le type de petits changements pour lesquels j'utilise ceci, par exemple les fautes de frappe dans les commentaires ou les noms de méthodes nouvellement introduites (ou renommées). Si ce n'est pas le cas, il affichera au moins une liste de commits candidats.
J'utilise beaucoup cela dans mon flux de travail quotidien, pour intégrer rapidement de petits changements aux lignes précédemment modifiées dans les commits sur ma branche de travail. Le script n'est pas aussi beau qu'il pourrait l'être, et il est écrit en zsh, mais il fait assez bien le travail pour moi depuis un bon moment maintenant que je n'ai jamais ressenti le besoin de le réécrire:
https://github.com/Valodim/git-fixup
la source
Vous pouvez créer une correction pour un fichier particulier en utilisant cet alias.
Si vous avez apporté des modifications dans
myfile.txt
mais que vous ne voulez pas les mettre dans un nouveau commit,git fixup-file myfile.txt
créera unfixup!
pour le commit où amyfile.txt
été modifié pour la dernière fois, puis il le serarebase --autosquash
.la source
git rebase
ne soit pas automatiquement appelé.commit --fixup
etrebase --autosquash
sont excellents, mais ils n'en font pas assez. Quand j'ai une séquence de commitsA-B-C
et que j'écris d'autres changements dans mon arbre de travail qui appartiennent à un ou plusieurs de ces commits existants, je dois regarder manuellement l'historique, décider quels changements appartiennent à quels commits, les mettre en scène et créer lefixup!
s'engage. Mais git a déjà accès à suffisamment d'informations pour pouvoir faire tout cela pour moi, j'ai donc écrit un script Perl qui fait exactement cela.Pour chaque morceau dans
git diff
le script utilisegit blame
pour trouver le commit qui a touché en dernier les lignes pertinentes, et appellegit commit --fixup
à écrire lesfixup!
commits appropriés , en faisant essentiellement la même chose que je faisais manuellement auparavant.Si vous le trouvez utile, n'hésitez pas à l'améliorer et à le répéter et peut-être qu'un jour nous obtiendrons une telle fonctionnalité
git
. J'aimerais voir un outil capable de comprendre comment un conflit de fusion doit être résolu lorsqu'il a été introduit par un rebase interactif.la source
J'ai écrit une petite fonction shell appelée
gcf
pour effectuer le commit de correction et le rebase automatiquement:Par exemple, vous pouvez patcher le deuxième commit avant le dernier avec:
gcf HEAD~~
Voici la fonction . Vous pouvez le coller dans votre
~/.bashrc
Il utilise
--autostash
pour cacher et afficher toutes les modifications non validées si nécessaire.--autosquash
nécessite un--interactive
rebase, mais nous évitons l'interaction en utilisant un mannequinEDITOR
.--no-fork-point
protège les commits d'être insérés en silence rares situations (lorsque vous avez bifurqué une nouvelle branche et que quelqu'un a déjà rebasé les commits passés).la source
Je ne connais pas de méthode automatisée, mais voici une solution qui pourrait être plus facile à botiser par l'homme:
la source
git fixup
commande.--autosquash
Je recommanderais https://github.com/tummychow/git-absorb :
la source