web-dev-qa-db-fra.com

Comment puis-je éditer un ancien message git commit par programme?

Vous pouvez modifier par programme uniquement le dernier message de validation:

git commit --amend -m 'xxxxxxx'

Ou un commit aléatoire interactif:

git rebase -i HEAD~n
# Vim opens up, select the commit you want to modify, and change the Word "pick" for "edit"
git commit --amend -m "Changing an old commit message!"
git rebase --continue

Comment combiner les deux? Je veux changer un message par programme, mais par un commit précédent, pas seulement le dernier.

La validation que je souhaite modifier a déjà été transmise à un serveur git, mais le fait de synchroniser à nouveau le projet git ne pose pas de problème. 

7
Jesus H

Si vous ne modifiez que quelques commits, utilisez git rebase -i et l’option "reword". Par exemple...

pick 6256642 mv file1 file2
pick 20c2e82 Add another line to file2

# Rebase 8236784..20c2e82 onto 8236784 (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using Shell
# d, drop = remove commit

Si vous passez pick à reword, un éditeur vous proposera de réécrire le message de validation.


Si vous devez faire la même chose avec beaucoup de commits, utilisez git filter-branch avec un --msg-filter. Le message de validation d'origine est sur stdin, le nouveau message de validation est sur stdout. En voici une pour changer "couleur" en "couleur" dans tous les commits de la branche actuelle.

git filter-branch --msg-filter "Perl -ple 's{color}{colour}g'"
5
Schwern

La raison pour laquelle vous ne pouvez pas simplement "modifier" un commit arbitraire est que les commits sont immuables. Lorsque vous modifiez un commit, celui-ci remplace le commit actuel par un autre et déplace votre branche vers le nouveau commit. Le commit avec l'ancien message, le nom de l'auteur, etc. est toujours présent dans l'historique jusqu'à ce que vous le nettoyiez:

Before:

        master
          |
          v
A -- B -- C

After:

        master
          |
          v
A -- B -- C'
      \
       \- C

Pour simuler "modifier" un commit arbitraire, vous devez réécrire non seulement ce commit, mais aussi l'historique complet qui le suit, puisqu'un commit inclut ses parents dans ses données immuables:

Before:

        master
          |
          v
A -- B -- C

After:

         master
           |
           v
A -- B' -- C'
 \ 
  \- B --- C

Pour ce faire, vous pouvez créer une branche sur le commit qui vous intéresse, la modifier et redéfinir la plage des commits en suivant l'original jusqu'au sommet de votre branche d'origine sur la nouvelle branche. Voici un exemple montrant ce que vous recherchez:

Start:

             master
               |
               v
A -- B -- C -- D

New Branch:

             master
               |
               v
A -- B -- C -- D
     ^
     |
    temp

Amend:

             master
               |
               v
A -- B -- C -- D
 \
  \- B'
     ^
     |
    temp

Rebase:

A -- B  -- C  -- D
 \
  \- B' -- C' -- D'
     ^           ^
     |           |
    temp       master

Cleanup:

A -- B  -- C  -- D
 \
  \- B' -- C' -- D'
                 ^
                 |
               master

C’est à peu près exactement ce que fait rebase interactive lorsque vous ne modifiez qu’une seule validation, en passant, sauf sans la branche temporaire explicite.

4
Mad Physicist

Étant donné que vous souhaitez effectuer le changement par programme, la refonte interactive (git rebase -i) n'est pas une option.

Editer un vieux commit, pour quelque raison que ce soit, rebase en fait tous les commits. Si vous ne modifiez que le message de validation, vous n'avez pas à vous soucier des conflits de fusion.

Vous créez une nouvelle branche temporaire avec la validation cible comme HEAD, modifiez le message de validation, fusionnez l'ancienne branche sur la nouvelle, puis supprimez l'ancienne branche temporaire.

Dans un script shell:

CURBRANCH=`git rev-parse --abbrev-ref HEAD`
TMPBRANCH=tmp$$
git checkout $SHA -b $TMPBRANCH
MSG=`tempfile`
git log --format=%B -n 1 HEAD > $MSG
... edit the $MSG file
git commit --amend -F $MSG
SHA=`git rev-list -n 1 HEAD`   # Commit has change, so SHA has also changed
rm $MSG
git rebase --onto $TMPBRANCH HEAD $CURBRANCH
git branch -d $TMPBRANCH
0
Timur Tabi