Comment changer le nom et l'e-mail de l'auteur et du committer de plusieurs commits dans Git?

2393

J'écrivais un script simple dans l'ordinateur de l'école et j'engageais les modifications dans Git (dans un dépôt qui était dans ma clé USB, cloné depuis mon ordinateur à la maison). Après plusieurs validations, j'ai réalisé que je validais des choses en tant qu'utilisateur root.

Existe-t-il un moyen de changer l'auteur de ces commits en mon nom?

Flávio Amieiro
la source
13
Question: l'utilisation de git filter-branch préserve-t-elle les SHA1 pour les balises, versions et objets précédents? Ou le changement du nom de l'auteur forcera-t-il également les SHA1 associés?
AndyL
36
Les hachages changeront oui
Non disponible du
3
Tangentiellement, j'ai créé un petit script qui a finalement corrigé la cause première pour moi. gist.github.com/tripleee/16767aa4137706fd896c
tripleee
2
@impinball L'âge de la question n'est guère pertinent. Créer une nouvelle question en double est hors de question. Je suppose que je pourrais créer une question qui appelle cette réponse particulière, mais je ne suis pas tout à fait convaincu qu'il obtiendrait autant de visibilité. Ce n'est pas comme s'il y avait une pénurie de questions sur Git ici ... Heureux d'avoir pu aider, de toute façon.
tripleee
8
GitHub a un script spécial pour cela: help.github.com/articles/changing-author-info
Timur Bernikovich

Réponses:

1214

Cette réponse utilise git-filter-branch, pour laquelle les documents donnent maintenant cet avertissement:

git filter-branch a une pléthore d'embûches qui peuvent produire des déformations non évidentes de la réécriture de l'histoire prévue (et peuvent vous laisser peu de temps pour enquêter sur de tels problèmes car il a une telle performance abyssale). Ces problèmes de sécurité et de performances ne peuvent pas être résolus de manière rétrocompatible et en tant que tels, son utilisation n'est pas recommandée. Veuillez utiliser un autre outil de filtrage d'historique tel que git filter-repo . Si vous devez encore utiliser git filter-branch, veuillez lire attentivement la SÉCURITÉ (et les PERFORMANCES ) pour en savoir plus sur les mines terrestres de filter-branch, puis éviter avec autant de vigilance que possible les dangers énumérés dans la liste.

Changer l'auteur (ou le committer) nécessiterait de réécrire toute l'histoire. Si vous êtes d'accord avec cela et pensez que cela en vaut la peine, alors vous devriez vérifier git filter-branch . La page de manuel comprend plusieurs exemples pour vous aider à démarrer. Notez également que vous pouvez utiliser des variables d'environnement pour changer le nom de l'auteur, du committer, des dates, etc. - voir la section "Variables d'environnement" de la page de manuel git .

Plus précisément, vous pouvez corriger tous les mauvais noms d'auteur et e-mails pour toutes les branches et balises avec cette commande (source: aide GitHub ):

#!/bin/sh

git filter-branch --env-filter '
OLD_EMAIL="[email protected]"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="[email protected]"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
Pat Notz
la source
612
Github a un script public pour cette aide.github.com/articles/changing-author-info et cela fonctionne très bien!
defvol
34
Après avoir exécuté le script, vous pouvez supprimer la branche de sauvegarde en exécutant "git update-ref -d refs / original / refs / heads / master".
DR
7
@rodowi, il duplique tous mes commits.
Rafael Barros
6
@RafaelBarros les informations sur l'auteur (comme toute autre chose dans l'histoire) font partie de la clé sha du commit. Toute modification de l'historique est une réécriture conduisant à de nouveaux identifiants pour toutes les validations. Alors ne réécrivez pas sur un repo partagé ou assurez-vous que tous les utilisateurs en sont conscients ...
johannes
20
Résolu en utilisantgit push --force --tags origin HEAD:master
mcont
1577

REMARQUE: cette réponse modifie les SHA1, alors faites attention à l'utiliser sur une branche qui a déjà été poussée. Si vous souhaitez uniquement corriger l'orthographe d'un nom ou mettre à jour un ancien e-mail, git vous permet de le faire sans réécrire l'historique à l'aide de .mailmap. Voir mon autre réponse .

Utilisation d'Interactive Rebase

Vous pourriez faire

git rebase -i -p <some HEAD before all of your bad commits>

Ensuite, marquez tous vos mauvais commits comme "modifier" dans le fichier de rebase. Si vous souhaitez également modifier votre premier commit, vous devez l'ajouter manuellement comme première ligne dans le fichier de rebase (suivez le format des autres lignes). Ensuite, quand git vous demande de modifier chaque commit, faites

 git commit --amend --author "New Author Name <[email protected]>" 

modifier ou simplement fermer l'éditeur qui s'ouvre, puis faites

git rebase --continue

pour continuer le rebase.

Vous pouvez ignorer l'ouverture de l'éditeur ici en ajoutant --no-edit pour que la commande soit:

git commit --amend --author "New Author Name <[email protected]>" --no-edit && \
git rebase --continue

Validation unique

Comme certains commentateurs l'ont noté, si vous souhaitez simplement modifier le commit le plus récent, la commande rebase n'est pas nécessaire. Fais juste

 git commit --amend --author "New Author Name <[email protected]>"

Cela changera l'auteur en nom spécifié, mais le committer sera défini sur votre utilisateur configuré dans git config user.nameet git config user.email. Si vous souhaitez définir le committer sur quelque chose que vous spécifiez, cela définira à la fois l'auteur et le committer:

 git -c user.name="New Author Name" -c user.email=email@address.com commit --amend --reset-author

Remarque sur les validations de fusion

Il y avait un léger défaut dans ma réponse d'origine. S'il y a des validations de fusion entre l'actuel HEADet le vôtre <some HEAD before all your bad commits>, git rebaseles aplatiront (et au fait, si vous utilisez des requêtes Pull GitHub, il y aura une tonne de validations de fusion dans votre historique). Cela peut très souvent conduire à un historique très différent (car les modifications en double peuvent être "rebasées"), et dans le pire des cas, cela peut conduire à git rebasevous demander de résoudre des conflits de fusion difficiles (qui étaient probablement déjà résolus dans les validations de fusion). La solution consiste à utiliser l' -pindicateur to git rebase, qui préservera la structure de fusion de votre historique. La page de manuel pour git rebaseavertit que l'utilisation de -pet -ipeut entraîner des problèmes, maisBUGS Dans la section, il est indiqué que «la modification des validations et la reformulation de leurs messages de validation devraient fonctionner correctement.

J'ai ajouté -pà la commande ci-dessus. Dans le cas où vous modifiez simplement la validation la plus récente, ce n'est pas un problème.

asmeurer
la source
27
Idéal pour les commissions étranges - utile si vous vous associez et oubliez de changer l'auteur
mloughran
32
+1 pour avoir mentionné le cas d'utilisation du correctif d'erreur type: git commit --amend --author = username
Nathan Kidd
12
C'est parfait, mon cas d'utilisation le plus courant est que je m'assois à un autre ordinateur et que j'oublie de configurer l'auteur et que j'ai donc généralement moins de 5 validations à corriger.
Zitrax
57
git commit --amend --reset-authorfonctionne également une fois user.nameet user.emailest configuré correctement.
pts
14
Réécrire les informations de l'auteur sur toutes les validations après avoir <commit>utilisé user.nameet à user.emailpartir de ~/.gitconfig: exécuter git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', enregistrer, quitter. Pas besoin d'éditer!
ntc2
588

Vous pouvez également faire:

git filter-branch --commit-filter '
        if [ "$GIT_COMMITTER_NAME" = "<Old Name>" ];
        then
                GIT_COMMITTER_NAME="<New Name>";
                GIT_AUTHOR_NAME="<New Name>";
                GIT_COMMITTER_EMAIL="<New Email>";
                GIT_AUTHOR_EMAIL="<New Email>";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD

Remarque, si vous utilisez cette commande dans l'invite de commande Windows, vous devez utiliser à la "place de ':

git filter-branch --commit-filter "
        if [ "$GIT_COMMITTER_NAME" = "<Old Name>" ];
        then
                GIT_COMMITTER_NAME="<New Name>";
                GIT_AUTHOR_NAME="<New Name>";
                GIT_COMMITTER_EMAIL="<New Email>";
                GIT_AUTHOR_EMAIL="<New Email>";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi" HEAD
Rognon
la source
4
L'utilisation du filtre env n'est-elle pas la solution la plus simple? Je ne sais pas pourquoi cela obtient plus de votes, alors.
stigkj
3
Le lien est alors rompu. Comment transférer ces modifications vers un autre référentiel?
Russell
28
env-filter changera tous les commits. Cette solution permet un conditionnel.
user208769
5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"désolé mais où le -f-flag va être quand vous exécutez ce script deux fois. En fait, c'est dans la réponse de Brian, désolé pour les perturbations juste après que la branche de filtre soit la solution.
hhh
2
@ user208769 env-filter permet également un conditionnel; regardez ma réponse :-)
stigkj
559

Un liner, mais soyez prudent si vous avez un référentiel multi-utilisateurs - cela changera toutes les validations pour avoir le même (nouveau) auteur et committer.

git filter-branch -f --env-filter "GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='new@email'; GIT_COMMITTER_NAME='Newname'; GIT_COMMITTER_EMAIL='new@email';" HEAD

Avec des sauts de ligne dans la chaîne (ce qui est possible en bash):

git filter-branch -f --env-filter "
    GIT_AUTHOR_NAME='Newname'
    GIT_AUTHOR_EMAIL='new@email'
    GIT_COMMITTER_NAME='Newname'
    GIT_COMMITTER_EMAIL='new@email'
  " HEAD
Brian Gianforcaro
la source
Petit point, l'exportation est en fait superflue bien qu'elle ne fasse pas de mal. par exemple git-filter-branch --env-filter "GIT_AUTHOR_NAME = 'New name'; GIT_AUTHOR_EMAIL = 'New email'" HEAD.
Alec the Geek
4
Pourquoi réécrit-il toutes les validations si vous spécifiez HEADà la fin de la commande?
Nick Volynkin
1
Cela ne fonctionne pas pour mon référentiel bitbucket, une idée? Je fais un git push --force --tags origin 'refs/heads/*'après la commande conseillée
Olorin
1
La commande push pour cela est:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK
1
Soigné; cela conserve également les anciens horodatages.
DharmaTurtle
221

Cela se produit lorsque vous n'avez pas initialisé $ HOME / .gitconfig. Vous pouvez résoudre ce problème comme suit:

git config --global user.name "you name"
git config --global user.email [email protected]
git commit --amend --reset-author

testé avec la version 1.7.5.4 de git

lrkwz
la source
9
Cela fonctionne très bien sur le dernier commit. Agréable et simple. Cela ne doit pas être un changement global, en utilisant des --localœuvres aussi
Ben
Celui-ci a été le grand gagnant pour moi! La git commit --amend --reset-author --no-editcommande est particulièrement utile si vous avez créé des validations avec des informations d'auteur erronées, puis définissez l'auteur correct après coup via git config. J'ai enregistré mon $ $$ juste au moment où je devais mettre à jour mon e-mail.
ecbrodie
187

Pour un seul commit:

git commit --amend --author="Author Name <[email protected]>"

(extrait de la réponse de l'asmeurer)

bleuté
la source
14
mais ce n'est que si c'est le dernier commit
Richard
4
Selon git help commit, git commit --amendmodifie le commit à la «pointe de la branche actuelle» (qui est HEAD). Il s'agit généralement du commit le plus récent, mais vous pouvez le faire à tout moment en vérifiant d'abord ce commit avec git checkout <branch-name>ou git checkout <commit-SHA>.
Rory O'Kane
12
Mais si vous faites cela, toutes les validations qui ont déjà cette validation en tant que parent pointeront vers la mauvaise validation. Mieux vaut utiliser filter-branch à ce stade.
John Gietzen
3
@JohnGietzen: Vous pouvez rebaser les commits sur celui qui a été modifié pour corriger cela. Cependant, si vous effectuez une validation> 1, comme mentionné, la branche de filtrage sera probablement beaucoup plus facile.
Thanatos
5
Notez que ces changements ne authorcommitter
valident
179

Dans le cas où seules les premières validations ont de mauvais auteurs, vous pouvez tout faire à l'intérieur en git rebase -iutilisant la execcommande et la --amendvalidation, comme suit:

git rebase -i HEAD~6 # as required

qui vous présente la liste modifiable des commits:

pick abcd Someone else's commit
pick defg my bad commit 1
pick 1234 my bad commit 2

Ajoutez ensuite des exec ... --author="..."lignes après toutes les lignes avec de mauvais auteurs:

pick abcd Someone else's commit
pick defg my bad commit 1
exec git commit --amend --author="New Author Name <[email protected]>" -C HEAD
pick 1234 my bad commit 2
exec git commit --amend --author="New Author Name <[email protected]>" -C HEAD

enregistrer et quitter l'éditeur (pour exécuter).

Cette solution peut être plus longue à taper que certaines autres, mais elle est hautement contrôlable - je sais exactement ce qu'elle engage.

Merci à @asmeurer pour l'inspiration.

Alex Brown
la source
26
Certainement génial. Pouvez-vous le raccourcir en définissant user.name et user.email dans la configuration locale du référentiel, puis chaque ligne ne l'est que exec git commit --amend --reset-author -C HEAD?
Andrew
1
La réponse canonique, pour utiliser filter-branch, vient de supprimer refs / heads / master pour moi. +1 à votre solution contrôlable et modifiable. Merci!
jmtd
Pourquoi commencez-vous par Someone else's commitau lieu de my bad commit 1? J'ai juste essayé HEAD^^de modifier les 2 derniers commits, et cela a parfaitement fonctionné.
fredoverflow
3
À la place de git rebase -i HEAD^^^^^^vous pouvez également écriregit rebase -i HEAD~6
Patrick Schlüter
1
Veuillez noter que cela modifie l'horodatage des commits. Voir stackoverflow.com/a/11179245/1353267 pour revenir aux horodatages corrects
Samveen
111

Github a une belle solution , qui est le script shell suivant:

#!/bin/sh

git filter-branch --env-filter '

an="$GIT_AUTHOR_NAME"
am="$GIT_AUTHOR_EMAIL"
cn="$GIT_COMMITTER_NAME"
cm="$GIT_COMMITTER_EMAIL"

if [ "$GIT_COMMITTER_EMAIL" = "[email protected]" ]
then
    cn="Your New Committer Name"
    cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "[email protected]" ]
then
    an="Your New Author Name"
    am="Your New Author Email"
fi

export GIT_AUTHOR_NAME="$an"
export GIT_AUTHOR_EMAIL="$am"
export GIT_COMMITTER_NAME="$cn"
export GIT_COMMITTER_EMAIL="$cm"
'
Olivier Verdier
la source
5
Fonctionne parfaitement. J'ai juste eu git reset --hard HEAD^quelques fois sur les autres référentiels locaux pour les amener à une version antérieure, git pull-ed la version modifiée, et me voici sans aucune ligne contenant unknown <[email protected]>(j'ai adoré git's defaulting).
Alan Plum
1
Je ne peux pas pousser après ça. Dois-je utiliser "-f"?
Fish Monitor
9
Je l'ai fait git push -f. De plus, les dépôts locaux doivent être reclonés après cela.
Fish Monitor
Si vous devez exécuter le script shell sur une branche spécifique, vous pouvez changer la dernière ligne en: "'master..votre-nom-de-branche" (en supposant que vous avez ramifié de master).
Robert Kajic
Cliquez sur le lien <nice solution> car le script a été mis à jour
gxpr
82

Comme l'a mentionné docgnome, la réécriture de l'historique est dangereuse et brisera les référentiels des autres.

Mais si vous voulez vraiment le faire et que vous êtes dans un environnement bash (pas de problème sous Linux, sous Windows, vous pouvez utiliser git bash, fourni avec l'installation de git), utilisez git filter-branch :

git filter-branch --env-filter '
  if [ $GIT_AUTHOR_EMAIL = bad@email ];
    then GIT_AUTHOR_EMAIL=correct@email;
  fi;
export GIT_AUTHOR_EMAIL'

Pour accélérer les choses, vous pouvez spécifier une gamme de révisions que vous souhaitez réécrire:

git filter-branch --env-filter '
  if [ $GIT_AUTHOR_EMAIL = bad@email ];
    then GIT_AUTHOR_EMAIL=correct@email;
  fi;
export GIT_AUTHOR_EMAIL' HEAD~20..HEAD
svick
la source
2
Notez que cela laissera toutes les balises pointant vers les anciens commits. --tag-name-filter catest l'option "faire fonctionner".
Roman Starkov
@romkyns une idée sur la façon de changer les balises aussi?
Nick Volynkin
@NickVolynkin Oui, vous spécifiez --tag-name-filter cat. Cela aurait vraiment dû être le comportement par défaut.
Roman Starkov
48

Lorsque vous reprenez un commit non fusionné d'un autre auteur, il existe un moyen simple de gérer cela.

git commit --amend --reset-author

Ryanmt
la source
1
Pour un seul commit, et si vous voulez mettre votre nom d'utilisateur, c'est le moyen le plus simple.
Pedro Benevides du
7
Vous pouvez ajouter --no-editpour rendre cela encore plus facile, car généralement la plupart des gens voudront mettre à jour uniquement l'adresse e-mail et non le message de validation
PlagueHammer
Pouvez-vous, s'il vous plaît, partager la commande git pour simplement mettre à jour l'e-mail / nom d'utilisateur du dernier commit avec le nouveau
adi
Avez-vous essayé cela? Cela devrait être un effet secondaire de cela, sinon stackoverflow.com/a/2717477/654245 ressemble à un bon chemin.
Ryanmt
47

Vous pouvez l'utiliser comme un alias afin de pouvoir faire:

git change-commits GIT_AUTHOR_NAME "old name" "new name"

ou pour les 10 derniers commits:

git change-commits GIT_AUTHOR_EMAIL "[email protected]" "[email protected]" HEAD~10..HEAD

Ajoutez à ~ / .gitconfig:

[alias]
    change-commits = "!f() { VAR=$1; OLD=$2; NEW=$3; shift 3; git filter-branch --env-filter \"if [[ \\\"$`echo $VAR`\\\" = '$OLD' ]]; then export $VAR='$NEW'; fi\" $@; }; f "

Source: https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig

J'espère que c'est utile.

brauliobo
la source
"git: 'change-commits' n'est pas une commande git. Voir 'git --help'."
Native_Mobile_Arch_Dev
Après cette commande et cette synchronisation avec master, toutes les validations de l'historique sont dupliquées! Même d'autres utilisateurs :(
Vladimir
@Vladimir attendu, veuillez étudier l'évolution de l'histoire dans git
brauliobo
Pour moi, il semble fonctionner dans / bin / sh, j'ai donc dû remplacer le test spécifique à bash par un test [[ ]]compatible avec sh [ ](crochets simples). En plus ça marche très bien, merci!
Steffen Schwigon Il y a
39

Il s'agit d'une version plus élaborée de la version de @ Brian:

Pour changer l'auteur et le committer, vous pouvez le faire (avec des sauts de ligne dans la chaîne qui est possible dans bash):

git filter-branch --env-filter '
    if [ "$GIT_COMMITTER_NAME" = "<Old name>" ];
    then
        GIT_COMMITTER_NAME="<New name>";
        GIT_COMMITTER_EMAIL="<New email>";
        GIT_AUTHOR_NAME="<New name>";
        GIT_AUTHOR_EMAIL="<New email>";
    fi' -- --all

Vous pouvez obtenir l'une de ces erreurs:

  1. Le répertoire temporaire existe déjà
  2. Les références commençant par refs / original existent déjà
    (cela signifie qu'une autre branche de filtre a été exécutée précédemment sur le référentiel et que la référence de branche d'origine est sauvegardée à refs / original )

Si vous souhaitez forcer l'exécution malgré ces erreurs, ajoutez l' --forceindicateur:

git filter-branch --force --env-filter '
    if [ "$GIT_COMMITTER_NAME" = "<Old name>" ];
    then
        GIT_COMMITTER_NAME="<New name>";
        GIT_COMMITTER_EMAIL="<New email>";
        GIT_AUTHOR_NAME="<New name>";
        GIT_AUTHOR_EMAIL="<New email>";
    fi' -- --all

Une petite explication de l' -- --alloption pourrait être nécessaire: elle fait fonctionner la branche de filtre sur toutes les révisions sur toutes les références (qui inclut toutes les branches). Cela signifie, par exemple, que les balises sont également réécrites et visibles sur les branches réécrites.

Une "erreur" courante consiste à utiliser à la HEADplace, ce qui signifie filtrer toutes les révisions uniquement sur la branche actuelle . Et puis aucune balise (ou autres références) n'existerait dans la branche réécrite.

stigkj
la source
Félicitations pour avoir fourni une procédure qui change les commits sur toutes les références / branches.
Johnny Utahh
25

Une seule commande pour changer l'auteur des N derniers commits:

git rebase -i HEAD~4 -x "git commit --amend --author 'Author Name <[email protected]>' --no-edit"

REMARQUES

  • le --no-editdrapeau s'assure que le git commit --amendne demande pas de confirmation supplémentaire
  • lorsque vous utilisez git rebase -i, vous pouvez sélectionner manuellement les validations où changer l'auteur,

le fichier que vous éditez ressemblera à ceci:

pick 897fe9e simplify code a little
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit
pick dc18f70 bugfix
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit

Vous pouvez ensuite modifier certaines lignes pour voir où vous souhaitez modifier l'auteur. Cela vous donne un bon compromis entre l'automatisation et le contrôle: vous voyez les étapes qui se dérouleront, et une fois que vous aurez enregistré, tout sera appliqué en même temps.

Chris Maes
la source
Impressionnant! Je vous remercie!
Pablo Lalloni
J'ai utilisé HEAD ~ 8 et cela montre bien plus que les 8 derniers commits.
Bryan Bryce
1
@BryanBryce s'il y a des validations de fusion impliquées, les choses se compliquent :)
Chris Maes
@ChrisMaes Ah, je vois ce qui se passe. Je ne veux pas jouer avec eux, juste sur la branche sur laquelle je suis.
Bryan Bryce
Dans ce cas, en supposant que vous vous êtes séparé du maître, vous pourriez:git rebase -i master -x ...
Chris Maes
23
  1. courir git rebase -i <sha1 or ref of starting point>
  2. marquez tous les commits que vous souhaitez modifier avec edit(ou e)
  3. boucle les deux commandes suivantes jusqu'à ce que vous ayez traité toutes les validations:

    git commit --amend --reuse-message=HEAD --author="New Author <[email protected]>" ; git rebase --continue

Cela conservera toutes les autres informations de validation (y compris les dates). L' --reuse-message=HEADoption empêche l'éditeur de messages de se lancer.

sporsh
la source
23

J'utilise ce qui suit pour réécrire l'auteur pour un référentiel entier, y compris les balises et toutes les branches:

git filter-branch --tag-name-filter cat --env-filter "
  export GIT_AUTHOR_NAME='New name';
  export GIT_AUTHOR_EMAIL='New email'
" -- --all

Ensuite, comme décrit dans la page MAN de filter-branch , supprimez toutes les références d'origine sauvegardées par filter-branch(c'est destructif, sauvegardez d'abord):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Ton van den Heuvel
la source
2
C'est très important à utiliser --tag-name-filter cat. Sinon, vos balises resteront sur la chaîne de validations d'origine. Les autres réponses ne le mentionnent pas.
jeberle
21

J'ai adapté cette solution qui fonctionne en ingérant un simple author-conv-file(le format est le même que pour git-cvsimport ). Il fonctionne en changeant tous les utilisateurs tels que définis dans author-conv-filetoutes les branches.

Nous l'avons utilisé conjointement avec cvs2gitpour migrer notre référentiel de cvs vers git.

c.-à-d. échantillon author-conv-file

john=John Doe <[email protected]>
jill=Jill Doe <[email protected]>

Le scénario:

 #!/bin/bash

 export $authors_file=author-conv-file

 git filter-branch -f --env-filter '

 get_name () {
     grep "^$1=" "$authors_file" |
     sed "s/^.*=\(.*\) <.*>$/\1/"
 }

 get_email () {
     grep "^$1=" "$authors_file" |
     sed "s/^.*=.* <\(.*\)>$/\1/"
 }

 GIT_AUTHOR_NAME=$(get_name $GIT_COMMITTER_NAME) &&
     GIT_AUTHOR_EMAIL=$(get_email $GIT_COMMITTER_NAME) &&
     GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
     GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL &&
     export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
     export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
 ' -- --all
Leif Gruenwoldt
la source
Merci, je me demande pourquoi ce n'est pas une fonctionnalité de base git (ou git-svn). Cela peut être fait avec un drapeau pour le clone git svn, mais pas dans la branche de filtre git ...
Daniel Hershcovich
20

Je dois souligner que si le seul problème est que l'auteur / e-mail est différent de votre habituel, ce n'est pas un problème. La solution correcte consiste à créer un fichier appelé .mailmapà la base du répertoire avec des lignes comme

Name you want <email you want> Name you don't want <email you don't want>

Et à partir de là, des commandes comme git shortlogconsidéreront ces deux noms comme étant les mêmes (sauf si vous leur dites spécifiquement de ne pas le faire). Voir http://schacon.github.com/git/git-shortlog.html pour plus d'informations.

Cela a l'avantage de toutes les autres solutions ici en ce que vous n'avez pas à réécrire l'historique, ce qui peut causer des problèmes si vous avez un amont, et c'est toujours un bon moyen de perdre accidentellement des données.

Bien sûr, si vous avez commis quelque chose comme vous-même et que cela devrait vraiment être quelqu'un d'autre, et que cela ne vous dérange pas de réécrire l'histoire à ce stade, changer l'auteur de la validation est probablement une bonne idée à des fins d'attribution (auquel cas je vous dirige vers mon autre réponse ici).

asmeurer
la source
18

J'ai trouvé que les versions présentées étaient agressives, surtout si vous validez des correctifs d'autres développeurs, cela volera essentiellement leur code.

La version ci-dessous fonctionne sur toutes les branches et modifie l'auteur et le comitter séparément pour éviter cela.

Félicitations à leif81 pour l'option tout.

#!/bin/bash

git filter-branch --env-filter '
if [ "$GIT_AUTHOR_NAME" = "<old author>" ];
then
    GIT_AUTHOR_NAME="<new author>";
    GIT_AUTHOR_EMAIL="<[email protected]>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
    GIT_COMMITTER_NAME="<new commiter>";
    GIT_COMMITTER_EMAIL="<[email protected]>";
fi
' -- --all
drahnr
la source
18
  1. Modifiez la validation author name & emailpar Amend, puis remplacez old-commit with new-one:

    $ git checkout <commit-hash>                            # checkout to the commit need to modify  
    $ git commit --amend --author "name <[email protected]>" # change the author name and email
    
    $ git replace <old-commit-hash> <new-commit-hash>      # replace the old commit by new one
    $ git filter-branch -- --all                           # rewrite all futures commits based on the replacement                   
    
    $ git replace -d <old-commit-hash>     # remove the replacement for cleanliness 
    $ git push -f origin HEAD              # force push 
    
  2. Une autre façon Rebasing:

    $ git rebase -i <good-commit-hash>      # back to last good commit
    
    # Editor would open, replace 'pick' with 'edit' before the commit want to change author
    
    $ git commit --amend --author="author name <[email protected]>"  # change the author name & email
    
    # Save changes and exit the editor
    
    $ git rebase --continue                # finish the rebase
    
Sajib Khan
la source
2
Très belle réponse. J'aime que les modifications soient terminées depuis la mise à jour même pour nettoyer même les commits git
Aleks
12

La façon la plus rapide et la plus simple de le faire est d'utiliser l'argument --exec de git rebase:

git rebase -i -p --exec 'git commit --amend --reset-author --no-edit'

Cela créera une liste de tâches qui ressemble à ceci:

pick ef11092 Blah blah blah
exec git commit --amend --reset-author --no-edit
pick 52d6391 Blah bloh bloo
exec git commit --amend --reset-author --no-edit
pick 30ebbfe Blah bluh bleh
exec git commit --amend --reset-author --no-edit
...

et cela fonctionnera automatiquement, ce qui fonctionne lorsque vous avez des centaines de validations.

Lie Ryan
la source
9

Si vous êtes le seul utilisateur de ce référentiel, vous pouvez réécrire l'historique en utilisant git filter-branch(comme écrit svick ), ou git fast-export/ ou un git fast-importscript de filtre (comme décrit dans l'article référencé dans la réponse docgnome ), ou un rebase interactif . Mais l'un ou l'autre modifierait les révisions à partir du premier commit modifié; cela signifie des problèmes pour quiconque a basé ses modifications sur la pré-réécriture de votre branche.

RÉCUPÉRATION

Si d'autres développeurs ne basaient pas leur travail sur la version de pré-réécriture, la solution la plus simple serait de re-cloner (cloner à nouveau).

Alternativement, ils peuvent essayer git rebase --pull, qui avancerait rapidement s'il n'y avait aucun changement dans leur référentiel, ou rebaser leur branche au-dessus des validations réécrites (nous voulons éviter la fusion, car cela garderait les comits de pré-réécriture pour toujours). Tout cela en supposant qu'ils n'ont pas engagé de travail; utiliser git stashpour cacher les modifications autrement.

Si d'autres développeurs utilisent des branches de fonctionnalités et / ou git pull --rebasene fonctionnent pas, par exemple parce que l'amont n'est pas configuré, ils doivent rebaser leur travail en plus des validations post-réécriture. Par exemple, juste après avoir récupéré de nouveaux changements ( git fetch), pour une masterbranche basée sur / forked from origin/master, il faut exécuter

$ git rebase --onto origin/master origin/master@{1} master

Voici l'état de origin/master@{1}pré-réécriture (avant l'extraction), voir gitrevisions .


Une autre solution serait d'utiliser refs / replace / mécanisme, disponible dans Git depuis la version 1.6.5. Dans cette solution, vous fournissez des remplacements pour les validations contenant un mauvais e-mail; alors quiconque récupère les références de remplacement (quelque chose comme fetch = +refs/replace/*:refs/replace/*refspec à l'endroit approprié dans leur .git/config ) obtiendrait des remplacements de manière transparente, et ceux qui ne récupèrent pas ces références verraient les anciens commits.

La procédure ressemble à ceci:

  1. Rechercher toutes les validations avec un mauvais e-mail, par exemple en utilisant

    $ git log [email protected] --all
    
  2. Pour chaque commit incorrect, créez un commit de remplacement et ajoutez-le à la base de données d'objets

    $ git cat-file -p <ID of wrong commit> | 
      sed -e 's/user@wrong\.email/[email protected]/g' > tmp.txt
    $ git hash-object -t commit -w tmp.txt
    <ID of corrected commit>
    
  3. Maintenant que vous avez corrigé la validation dans la base de données d'objets, vous devez dire à git de remplacer automatiquement et de manière transparente la mauvaise validation par une correction à l'aide de la git replacecommande:

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Enfin, listez tous les remplaçants pour vérifier si cette procédure a réussi

    $ git replace -l
    

    et vérifier si des remplacements ont lieu

    $ git log [email protected] --all
    

Vous pouvez bien sûr automatiser cette procédure ... eh bien, tout sauf en utilisant git replacequi n'a pas (encore) le mode batch, vous devrez donc utiliser la boucle shell pour cela, ou remplacer "à la main".

PAS TESTÉ! YMMV.

Notez que vous pourriez rencontrer des coins difficiles lors de l'utilisation du refs/replace/mécanisme: il est nouveau et pas encore très bien testé .

Jakub Narębski
la source
6

Si les validations que vous souhaitez corriger sont les dernières, et seulement quelques-unes, vous pouvez utiliser une combinaison de git resetet git stashrevenir en arrière et les valider à nouveau après avoir configuré le bon nom et l'adresse e-mail.

La séquence ressemblera à ceci (pour 2 commits incorrects, aucun changement en attente):

git config user.name <good name>
git config user.email <good email>
git reset HEAD^
git stash
git reset HEAD^
git commit -a
git stash pop
git commit -a
djromero
la source
5

Si vous utilisez Eclipse avec EGit, il existe une solution assez simple.
Hypothèse: vous avez des commits dans une branche locale 'local_master_user_x' qui ne peuvent pas être poussés vers une branche distante 'master' à cause de l'utilisateur invalide.

  1. Commander la branche distante «maître»
  2. Sélectionnez les projets / dossiers / fichiers pour lesquels 'local_master_user_x' contient des modifications
  3. Clic droit - Remplacer par - Branche - 'local_master_user_x'
  4. Validez à nouveau ces modifications, cette fois en tant qu'utilisateur correct et dans la branche locale «maître»
  5. Pousser vers le «maître» distant
paphko
la source
5

À l'aide de rebase interactif, vous pouvez placer une commande de modification après chaque validation que vous souhaitez modifier. Par exemple:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
j16r
la source
3
Le problème avec cela est que d'autres métadonnées de validation (par exemple la date et l'heure) sont également modifiées. Je viens de découvrir cela à la dure ;-).
halfer
5

Notez que git stocke deux adresses e-mail différentes, une pour le committer (la personne qui a commis le changement) et une autre pour l' auteur (la personne qui a écrit le changement).

Les informations sur le committer ne sont pas affichées dans la plupart des endroits, mais vous pouvez les voir avec git log -1 --format=%cn,%ce(ou utiliser showau lieu de logpour spécifier un commit particulier).

Bien que changer l'auteur de votre dernier commit soit aussi simple que cela git commit --amend --author "Author Name <[email protected]>", il n'y a pas de ligne ou d'argument unique pour faire de même avec les informations du committer.

La solution consiste à (temporairement ou non) modifier vos informations utilisateur, puis à modifier le commit, ce qui mettra le commiteur à jour avec vos informations actuelles:

git config user.email [email protected] 
git commit --amend
Sir Athos
la source
Notez que l'ancienne valeur est toujours présente à quelques endroits path\to\repo\.git. Je ne sais pas encore ce que vous devez faire pour l'effacer totalement. Amends malheureusement (?) Ne semble pas effacer.
ruffin
5

Nous avons rencontré un problème aujourd'hui où un caractère UTF8 dans un nom d'auteur causait des problèmes sur le serveur de génération, nous avons donc dû réécrire l'historique pour corriger cela. Les mesures prises étaient les suivantes:

Étape 1: Modifiez votre nom d'utilisateur dans git pour tous les futurs commits, selon les instructions ici: https://help.github.com/articles/setting-your-username-in-git/

Étape 2: exécutez le script bash suivant:

#!/bin/sh

REPO_URL=ssh://path/to/your.git
REPO_DIR=rewrite.tmp

# Clone the repository
git clone ${REPO_URL} ${REPO_DIR}

# Change to the cloned repository
cd ${REPO_DIR}

# Checkout all the remote branches as local tracking branches
git branch --list -r origin/* | cut -c10- | xargs -n1 git checkout

# Rewrite the history, use a system that will preseve the eol (or lack of in commit messages) - preferably Linux not OSX
git filter-branch --env-filter '
OLD_EMAIL="[email protected]"
CORRECT_NAME="New Me"

if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
fi
' --tag-name-filter cat -- --branches --tags

# Force push the rewritten branches + tags to the remote
git push -f

# Remove all knowledge that we did something
rm -rf ${REPO_DIR}

# Tell your colleagues to `git pull --rebase` on all their local remote tracking branches

Aperçu rapide: extrayez votre référentiel dans un fichier temporaire, extrayez toutes les branches distantes, exécutez le script qui réécrira l'historique, effectuez une poussée forcée du nouvel état et dites à tous vos collègues de faire un tirage de rebase pour obtenir les modifications.

Nous avons eu du mal à exécuter cela sur OS X, car cela a en quelque sorte gâché les fins de ligne dans les messages de validation, nous avons donc dû le réexécuter sur une machine Linux par la suite.

Miloš Ranđelović
la source
5

Votre problème est vraiment courant. Voir " Utiliser Mailmap pour corriger la liste des auteurs dans Git "

Par souci de simplicité, j'ai créé un script pour faciliter le processus: git-changemail

Après avoir mis ce script sur votre chemin, vous pouvez lancer des commandes comme:

  • Modifier les correspondances d'auteur sur la branche actuelle

    $ git changemail -a [email protected] -n newname -m [email protected]
    
  • Modifiez les correspondances d'auteur et de committer sur <branch> et <branch2>. Passer -fà filter-branch pour permettre la réécriture des sauvegardes

    $ git changemail -b [email protected] -n newname -m [email protected] -- -f &lt;branch> &lt;branch2>
    
  • Afficher les utilisateurs existants sur le référentiel

    $ git changemail --show-both
    

Au fait, après avoir apporté vos modifications, nettoyez la sauvegarde de la branche de filtre avec: git-backup-clean

albfan
la source
1
quand j'exécute votre commande, il dit "fatal: impossible d'exécuter 'git-changemail': autorisation refusée"
Govind
3

Je veux également ajouter mon exemple. Je veux créer une fonction bash avec un paramètre donné.

cela fonctionne dans mint-linux-17.3

# $1 => email to change, $2 => new_name, $3 => new E-Mail

function git_change_user_config_for_commit {

 # defaults
 WRONG_EMAIL=${1:-"[email protected]"}
 NEW_NAME=${2:-"your name"}
 NEW_EMAIL=${3:-"[email protected]"}

 git filter-branch -f --env-filter "
  if [ \$GIT_COMMITTER_EMAIL = '$WRONG_EMAIL' ]; then
    export GIT_COMMITTER_NAME='$NEW_NAME'
    export GIT_COMMITTER_EMAIL='$NEW_EMAIL'
  fi
  if [ \$GIT_AUTHOR_EMAIL = '$WRONG_EMAIL' ]; then
    export GIT_AUTHOR_NAME='$NEW_NAME'
    export GIT_AUTHOR_EMAIL='$NEW_EMAIL'
  fi
 " --tag-name-filter cat -- --branches --tags;
}
stephanfriedrich
la source
2

Si vous êtes le seul utilisateur de ce dépôt ou si vous ne vous souciez pas de le casser pour d'autres utilisateurs, alors oui. Si vous avez poussé ces validations et qu'elles existent là où un autre endroit peut y accéder, alors non, à moins que vous ne vous souciez pas de casser les repos des autres. Le problème est qu'en changeant ces validations, vous générerez de nouveaux SHA qui les traiteront comme des validations différentes. Quand quelqu'un d'autre essaie de récupérer ces commits modifiés, l'histoire est différente et kaboom.

Cette page http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html décrit comment procéder. (Je n'ai pas essayé ça alors YMMV)

baudtack
la source
Il n'existe donc aucun moyen sûr de réécrire le user.email. Sans faire exploser tout le monde. Je savais que réécrire l'histoire était une mauvaise idée, je pensais juste qu'il pourrait y avoir un moyen propre de le faire en toute sécurité. Merci.
manumoomoo
@mediaslave: Essayez le refs/replace/mécanisme.
Jakub Narębski
meta.stackexchange.com/a/8259/184684 - alias, additionnez les liens pour en faire des réponses.
ruffin