187 votes

Comment puis-je réparer facilement un commit passé?

Je viens de lire modifiant un seul fichier dans un passé, s'engager dans git , mais malheureusement, la solution retenue 'trie' le commet, ce qui n'est pas ce que je veux. Alors, voici ma question:

Chaque maintenant et puis, j'ai remarqué un bug dans mon code tout en travaillant sur un (indépendantes) de fonctionnalité. Un rapide git blame révèle alors que le bug a été introduit un peu commet il y a peu (j'ai commis beaucoup, donc en général c'est pas la plus récente commit qui a introduit le bug). À ce stade, j'ai l'habitude de le faire:

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 est plus ennuyeux. Surtout le "rebase interactif" est ennuyeux. Est-il un raccourci pour la séquence ci-dessus, ce qui me permet de modifier l'arbitraire, de commettre dans le passé avec la mise en scène des changements? Je suis parfaitement conscient que cela change l'histoire, mais je suis en train de faire 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 smart script qui peut réécrire s'engage à l'aide d'outils de plomberie?

276voto

Frerich Raabe Points 23711

Mise à JOUR: il y a UN moment, un nouveau --fixup argument a été ajouté à l' git commit qui peut être utilisé pour construire un commit avec un message de log adapté pour git rebase --interactive --autosquash. Donc, la façon la plus simple afin de corriger une dernière validation est maintenant:

$ git add ...                           # Stage a fix
$ git commit --fixup=a0b1c2d3           # Perform the commit to fix broken a0b1c2d3
$ git rebase -i --autosquash a0b1c2d3~1 # Now merge fixup commit into broken commit

Voici un petit script Python que j'ai écrit il y a un moment qui met en œuvre cette git fixup logique, j'espérais dans ma question initiale. Le script suppose que vous avez mis en scène certains des changements et s'applique à ces changements afin de le commettre.

REMARQUE: Ce script est spécifique à Windows; il semble pour l' git.exe et définit l' GIT_EDITOR variable d'environnement à l'aide de set. Ajuster selon les besoins pour d'autres systèmes d'exploitation.

À l'aide de ce script, je peux mettre en œuvre précisément la " réparation de sources, le stade de bugs, lancez la commande git correction de flux de travail que j'ai demandé:

#!/usr/bin/env python
from subprocess import call
import sys

# Taken from http://stackoverflow.com/questions/377017/test-if-executable-exists-in python
def which(program):
    import os
    def is_exe(fpath):
        return os.path.exists(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file

    return None

if len(sys.argv) != 2:
    print "Usage: git fixup <commit>"
    sys.exit(1)

git = which("git.exe")
if not git:
    print "git-fixup: failed to locate git executable"
    sys.exit(2)

broken_commit = sys.argv[1]
if call([git, "rev-parse", "--verify", "--quiet", broken_commit]) != 0:
    print "git-fixup: %s is not a valid commit" % broken_commit
    sys.exit(3)

if call([git, "diff", "--staged", "--quiet"]) == 0:
    print "git-fixup: cannot fixup past commit; no fix staged."
    sys.exit(4)

if call([git, "diff", "--quiet"]) != 0:
    print "git-fixup: cannot fixup past commit; working directory must be clean."
    sys.exit(5)

call([git, "commit", "--fixup=" + broken_commit])
call(["set", "GIT_EDITOR=true", "&&", git, "rebase", "-i", "--autosquash", broken_commit + "~1"], shell=True)

52voto

Kris Jenkins Points 2229

Ce que je fais, c'est:

git add ... # ajouter le correctif.
git commit # Commis, mais dans le mauvais endroit.
git rebase -i de la TÊTE de~5 n ° d'Examiner les 5 dernières s'engage pour la relocalisation.

Votre éditeur s'ouvre avec une liste des 5 derniers commits, prêt à être touché. Changement:

choisissez 08e833c Bon changement 1.
choisissez 9134ac9 Bon changement 2.
choisissez 5adda55 Mal de changement!
choisissez 400bce4 Bon changement 3.
choisissez 2bc82n1 Corriger de mauvais changement.

...:

choisissez 08e833c Bon changement 1.
choisissez 9134ac9 Bon changement 2.
choisissez 5adda55 Mal de changement!
f 2bc82n1 Corriger de mauvais changement.  # Déplacer vers le haut, et de changer de 'choisir' à 'f' pour 'correction'.
choisissez 400bce4 Bon changement 3.

Enregistrez et quittez votre éditeur, et le correctif sera reléguée à l'arrière dans la livraison, il appartient.

Après vous avez fait cela à quelques reprises, vous allez le faire en quelques secondes dans votre sommeil. Interactive changement d'année de base est la fonctionnalité qui vraiment m'a vendu sur git. Il est incroyablement utile pour cela et bien plus...

32voto

dschlyter Points 51

Un peu en retard à la fête, mais voici une solution qui fonctionne comme l'auteur l'a imaginé.

Ajoutez ceci à votre .gitconfig:

[alias]
    fixup = "!sh -c '(git diff-files --quiet || (echo Unstaged changes, please commit or stash with --keep-index; exit 1)) && COMMIT=$(git rev-parse $1) && git commit --fixup=$COMMIT && git rebase -i --autosquash $COMMIT~1' -"

Exemple d'utilisation:

git add -p
git fixup HEAD~5

Toutefois, si vous avez unstaged modifications, vous devez les ranger avant le rebase.

git add -p
git stash --keep-index
git fixup HEAD~5
git stash pop

Vous pouvez modifier l'alias de ranger automatiquement, au lieu de donner un avertissement. Toutefois, si la correction ne s'applique pas correctement, vous aurez besoin de la pop le coffre manuellement après la fixation de la conflits. Faire à la fois l'économie et de popping manuellement semble plus cohérent et moins à confusion.

8voto

Deiwin Points 232

J'ai été à la recherche de quelque chose de similaire. Ce script Python semble trop compliqué, bien que, par conséquent, j'ai cloué ma propre solution:

Tout d'abord, mon git alias ressembler à ça (comme ici):

[alias]
  fixup = !sh -c 'git commit --fixup=$1' -
  squash = !sh -c 'git commit --squash=$1' -
  ri = rebase --interactive --autosquash

Maintenant, la fête de la fonction est assez simple:

function gf {
  if [ $# -eq 1 ]
  then
    if [[ "$1" == HEAD* ]]
    then
      git add -A; git fixup $1; git ri $1~2
    else
      git add -A; git fixup $1; git ri $1~1
    fi
  else
    echo "Usage: gf <commit-ref> "
  fi
}

Ce code premières étapes de la vie de tous les changements en cours(vous pouvez enlever cette partie, si vous le souhaitez de mettre en scène les fichiers vous-même). Crée ensuite la correction(squash peut également être utilisé, si c'est ce que vous avez besoin d') s'engager. Après cela, il commence un rebase interactif avec l' --autosquash drapeau sur le parent de l'engagement que vous donnez comme argument. Qui ouvrira votre configuré éditeur de texte, afin de vous permettre de vérifier que tout est comme vous vous attendez et simplement la fermeture de l'éditeur de terminer le processus.

L' if [[ "$1" == HEAD* ]] de la partie (comme ici) est utilisé, parce que si vous utilisez, par exemple, la TÊTE de~2 comme commit(le commit que vous voulez corriger les modifications en cours) de référence, puis la TÊTE seront déplacées suite à la correction de commettre a été créé et vous devez utiliser HEAD~3 pour désigner la même livraison.

0voto

Tobias Kienzler Points 3769

Je ne suis pas au courant d'une manière automatisée, mais voici une solution qui pourrait être plus facile à humainiser:

 git stash
# write the patch
git add -p <file>
git commit -m"whatever"   # message doesn't matter, will be replaced via 'fixup'
git rebase -i <bad-commit-id>~1
# now cut&paste the "whatever" line from the bottom to the second line
# (i.e. below <bad-commit>) and change its 'pick' into 'fixup'
# -> the fix commit will be merged into the <bad-commit> without changing the
# commit message
git stash pop
 

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