¿Cómo cambiar el autor y el nombre del confirmador y el correo electrónico de múltiples confirmaciones en Git?

2392

Estaba escribiendo un guión simple en la computadora de la escuela y cometiendo los cambios en Git (en un repositorio que estaba en mi pendrive, clonado desde mi computadora en casa). Después de varias confirmaciones, me di cuenta de que estaba comprometiendo cosas como usuario root.

¿Hay alguna forma de cambiar el autor de estas confirmaciones a mi nombre?

Flávio Amieiro
fuente
13
Pregunta: ¿el uso de git filter-branch conserva los SHA1 para etiquetas, versiones y objetos anteriores? ¿O cambiar la fuerza del nombre del autor también cambiará los SHA1 asociados?
AndyL
36
Hashes cambiará sí
No disponible
3
Tangencialmente, creé un pequeño script que finalmente solucionó la causa raíz para mí. gist.github.com/tripleee/16767aa4137706fd896c
tripleee
2
@impinball La edad de la pregunta no es relevante. Crear una nueva pregunta duplicada está fuera de discusión. Supongo que podría crear una pregunta que suplique esta respuesta en particular, pero no estoy del todo convencido de que obtenga tanta visibilidad. No es que haya una escasez de preguntas de Git aquí ... Me alegro de poder ayudar, de todos modos.
tripleee
8
GitHub tiene un script especial para esto: help.github.com/articles/changing-author-info
Timur Bernikovich

Respuestas:

1214

Esta respuesta utiliza git-filter-branch, para lo cual los documentos ahora dan esta advertencia:

git filter-branch tiene una gran cantidad de dificultades que pueden producir alteraciones no obvias de la reescritura de la historia prevista (y pueden dejarlo con poco tiempo para investigar tales problemas, ya que tiene un rendimiento tan abismal). Estos problemas de seguridad y rendimiento no se pueden solucionar de forma retrocompatible y, como tal, no se recomienda su uso. Utilice una herramienta alternativa de filtrado de historial como git filter-repo . Si aún necesita usar git filter-branch, lea detenidamente SEGURIDAD (y RENDIMIENTO ) para obtener información sobre las minas terrestres de filter-branch, y luego evite con precaución tantos peligros enumerados allí como sea razonablemente posible.

Cambiar el autor (o el confirmador) requeriría reescribir toda la historia. Si estás de acuerdo con eso y crees que vale la pena, entonces deberías revisar git filter-branch . La página del manual incluye varios ejemplos para comenzar. También tenga en cuenta que puede usar variables de entorno para cambiar el nombre del autor, el confirmador, las fechas, etc. - vea la sección "Variables de entorno" de la página de manual de git .

Específicamente, puede corregir todos los nombres de autor y correos electrónicos incorrectos para todas las ramas y etiquetas con este comando (fuente: ayuda de 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
fuente
612
¡Github tiene un script público para esa ayuda.github.com/articles/changing-author-info y funciona muy bien!
defvol
34
Después de ejecutar el script, puede eliminar la rama de respaldo ejecutando "git update-ref -d refs / original / refs / heads / master".
DR
77
@rodowi, duplica todos mis commits.
Rafael Barros
66
@RafaelBarros la información del autor (como cualquier otra cosa en la historia) es parte de la clave sha del commit. Cualquier cambio en el historial es una reescritura que conduce a nuevas identificaciones para todos los commits. Así que no reescriba en un repositorio compartido ni asegúrese de que todos los usuarios lo sepan ...
johannes
20
Resuelto usandogit push --force --tags origin HEAD:master
mcont
1577

NOTA: Esta respuesta cambia los SHA1, así que tenga cuidado al usarla en una rama que ya ha sido enviada. Si solo desea corregir la ortografía de un nombre o actualizar un correo electrónico antiguo, git le permite hacerlo sin tener que volver a escribir el historial .mailmap. Ver mi otra respuesta .

Usando Rebase Interactiva

Podrías hacerlo

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

Luego marque todas sus confirmaciones erróneas como "editar" en el archivo de rebase. Si también desea cambiar su primera confirmación, debe agregarla manualmente como primera línea en el archivo de rebase (siga el formato de las otras líneas). Luego, cuando git le pide que modifique cada confirmación, haga

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

editar o simplemente cerrar el editor que se abre, y luego hacer

git rebase --continue

para continuar el rebase.

Puede omitir abrir el editor por completo aquí agregando --no-edit para que el comando sea:

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

Compromiso único

Como han señalado algunos de los comentaristas, si solo desea cambiar la confirmación más reciente, el comando rebase no es necesario. Solo haz

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

Esto cambiará el autor al nombre especificado, pero el confirmador se establecerá en su usuario configurado en git config user.namey git config user.email. Si desea establecer el confirmador en algo que especifique, esto establecerá tanto el autor como el confirmador:

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

Nota sobre los compromisos de fusión

Hubo un pequeño defecto en mi respuesta original. Si hay ningún commit combinación entre la corriente HEADy su <some HEAD before all your bad commits>, a continuación, git rebasese aplanan ellos (y por cierto, si se utiliza una solicitud de extracción de GitHub, no va a haber un montón de confirmaciones de combinación en el historial). Esto a menudo puede conducir a un historial muy diferente (ya que los cambios duplicados pueden "eliminarse"), y en el peor de los casos, puede llevar a git rebasepedirle que resuelva conflictos de fusión difíciles (que probablemente ya se resolvieron en los compromisos de fusión). La solución es usar la -pbandera para git rebase, que preservará la estructura de fusión de su historial. La página de manual para git rebaseadvierte que el uso -py -ipuede conducir a problemas, pero en elBUGS la sección dice "Editar confirmaciones y volver a redactar sus mensajes de confirmación debería funcionar bien".

He agregado -pal comando anterior. Para el caso en el que solo está cambiando la confirmación más reciente, esto no es un problema.

asmeurer
fuente
27
Sin embargo, es genial para los
commits
32
+1 por mencionar el caso de uso de la solución típica de un error: git commit --amend --author = username
Nathan Kidd
12
Esto es perfecto, mi caso de uso más común es que me siento en otra computadora y olvido configurar el autor y, por lo tanto, generalmente tengo <5 commits más o menos para arreglar.
Zitrax
57
git commit --amend --reset-authorTambién funciona una vez user.namey user.emailestán configurados correctamente.
pts
14
Reescriba la información del autor en todos los commits después de <commit>usar user.namey user.emaildesde ~/.gitconfig: ejecutar git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', guardar, salir. No hay necesidad de editar!
ntc2
588

También puedes hacer:

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

Tenga en cuenta que si está usando este comando en el símbolo del sistema de Windows, debe usarlo en "lugar 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
fuente
44
¿No es el uso de env-filter la solución más fácil? No estoy seguro de por qué esto está obteniendo más votos, entonces.
stigkj
3
Entonces el enlace está roto. ¿Cómo llevamos estos cambios a otro repositorio?
Russell
28
env-filter cambiará todas las confirmaciones. Esta solución permite un condicional.
user208769
55
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"lo siento, pero dónde -fva a estar la bandera cuando se ejecute este script dos veces. En realidad, eso está en la respuesta de Brian, disculpen las molestias justo después de que la rama de filtro sea la solución.
hhh
2
@ user208769 env-filter también permite un condicional; mira mi respuesta :-)
stigkj
559

Una línea, pero tenga cuidado si tiene un repositorio multiusuario: esto cambiará todas las confirmaciones para tener el mismo (nuevo) autor y confirmador.

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

Con saltos de línea en la cadena (que es posible 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
fuente
Punto menor, la exportación es realmente superflua aunque no hace daño. por ejemplo, git-filter-branch --env-filter "GIT_AUTHOR_NAME = 'Nuevo nombre'; GIT_AUTHOR_EMAIL = 'Nuevo correo electrónico'" HEAD.
Alec the Geek
44
¿Por qué reescribe todas las confirmaciones si especificas HEADal final del comando?
Nick Volynkin
1
Esto no funciona para mi repositorio de bitbucket, ¿alguna idea? Hago un git push --force --tags origin 'refs/heads/*'después del comando recomendado
Olorin
1
El comando de empuje para esto es:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK
1
Ordenado; esto también mantiene las viejas marcas de tiempo.
DharmaTurtle
221

Ocurre cuando no tiene un $ HOME / .gitconfig inicializado. Puedes arreglar esto como:

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

probado con git versión 1.7.5.4

lrkwz
fuente
99
Eso funciona muy bien en la última confirmación. Agradable y simple No tiene que ser un cambio global, usar --localobras también
Ben
¡Este fue el gran ganador para mí! El git commit --amend --reset-author --no-editcomando es especialmente útil si creó confirmaciones con la información de autor incorrecta, luego configure el autor correcto después del hecho a través de git config. Ahorré un $$ ahora mismo cuando tuve que actualizar mi correo electrónico.
ecbrodie
187

Para una sola confirmación:

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

(extraído de la respuesta del autor)

azulado
fuente
14
pero eso es solo si es la confirmación más reciente
Richard
44
Según git help commit, git commit --amendcambia la confirmación en la "punta de la rama actual" (que es HEAD). Esta es normalmente la confirmación más reciente, pero puede hacer la confirmación que desee al verificar primero esa confirmación con git checkout <branch-name>o git checkout <commit-SHA>.
Rory O'Kane
12
Pero si hace eso, todos los commits que ya tienen ese commit como padre estarán apuntando al commit incorrecto. Es mejor usar filter-branch en ese punto.
John Gietzen
3
@JohnGietzen: puede volver a basar los commits en el que ha cambiado para arreglar eso. Sin embargo, si está haciendo> 1 commit, entonces, como se mencionó, filter-branch probablemente será mucho más fácil.
Thanatos
55
Tenga en cuenta que esto solo cambia commit authory no elcommitter
Nick Volynkin
179

En el caso de que solo los pocos commits superiores tengan malos autores, puede hacer todo esto dentro git rebase -iusando el execcomando y el --amendcommit, de la siguiente manera:

git rebase -i HEAD~6 # as required

que le presenta la lista editable de confirmaciones:

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

Luego agregue exec ... --author="..."líneas después de todas las líneas con malos autores:

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

guardar y salir del editor (para ejecutar).

Esta solución puede ser más larga de escribir que algunas otras, pero es altamente controlable: sé exactamente a qué compromete.

Gracias a @asmeurer por la inspiración.

Alex Brown
fuente
26
Definitivamente asombroso. ¿Puede acortarlo configurando user.name y user.email en la configuración local del repositorio, y luego cada línea es solo exec git commit --amend --reset-author -C HEAD?
Andrew
1
La respuesta canónica, para usar filter-branch, acaba de eliminar refs / heads / master para mí Entonces +1 a su solución controlable y editable. ¡Gracias!
jmtd
¿Por qué empiezas con en Someone else's commitlugar de my bad commit 1? Intenté HEAD^^enmendar los últimos 2 commits, y funcionó perfectamente bien.
fredoverflow
3
En lugar de git rebase -i HEAD^^^^^^usted también puede escribirgit rebase -i HEAD~6
Patrick Schlüter
1
Tenga en cuenta que esto cambia la marca de tiempo de las confirmaciones. Consulte stackoverflow.com/a/11179245/1353267 para volver a las marcas de tiempo correctas
Samveen
111

Github tiene una buena solución , que es el siguiente script de shell:

#!/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
fuente
55
Funcionó a la perfección. Solo tuve que hacerlo git reset --hard HEAD^un par de veces en los otros repositorios locales para obtener una versión anterior, git pull-ed la versión enmendada, y aquí estoy sin ninguna línea que contenga unknown <[email protected]>(me encantó el incumplimiento de git).
Alan Plum
1
No puedo empujar después de esto. ¿Tengo que usar "-f"?
Fish Monitor
99
Lo hice git push -f. Además, los repositorios locales deben volverse a clasificar después de esto.
Monitor de peces
Si necesita ejecutar el script de shell en una rama específica, puede cambiar la última línea en: "'master..your-branch-name" (suponiendo que se bifurcó de master).
Robert Kajic
Haga clic en el enlace <buena solución> a medida que se actualiza el script
gxpr
82

Como mencionó docgnome, reescribir el historial es peligroso y romperá los repositorios de otras personas.

Pero si realmente quieres hacer eso y estás en un entorno bash (no hay problema en Linux, en Windows, puedes usar git bash, que se proporciona con la instalación de git), usa 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'

Para acelerar las cosas, puede especificar un rango de revisiones que desea reescribir:

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
fuente
2
Tenga en cuenta que esto dejará las etiquetas apuntando a las confirmaciones anteriores. --tag-name-filter cates la opción "hacer que funcione".
Roman Starkov
@romkyns alguna idea sobre cómo cambiar las etiquetas también?
Nick Volynkin
@NickVolynkin Sí, usted especifica --tag-name-filter cat. Esto realmente debería haber sido el comportamiento predeterminado.
Roman Starkov
48

Al hacerse cargo de un commit no fusionado de otro autor, hay una manera fácil de manejar esto.

git commit --amend --reset-author

Ryanmt
fuente
1
Para un solo commit, y si quieres poner tu nombre de usuario, esta es la manera más fácil.
Pedro Benevides el
77
Se pueden añadir --no-edita hacer esto aún más fácil, ya que por lo general la mayoría de la gente quiere actualizar sólo la dirección de correo electrónico y no el mensaje de confirmación
PlagueHammer
¿Pueden compartir el comando git solo para actualizar el correo electrónico / nombre de usuario de la última confirmación con el nuevo?
adi
¿Intentaste esto? Eso debería ser un efecto secundario de esto, si no stackoverflow.com/a/2717477/654245 parece un buen camino.
Ryanmt
47

Puede usar esto como un alias para que pueda hacer:

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

o para los últimos 10 commits:

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

Añadir a ~ / .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 "

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

Espero que sea útil.

brauliobo
fuente
"git: 'change-commits' no es un comando git. Ver 'git --help'".
Native_Mobile_Arch_Dev
¡Después de este comando y sincronización con el maestro, todas las confirmaciones en el historial se duplican! Incluso de otros usuarios :(
Vladimir
@Vladimir que se espera, estudie sobre el cambio de la historia en git
brauliobo
Para mí, parece ejecutarse en / bin / sh, así que tuve que reemplazar la prueba específica de bash por una prueba [[ ]]compatible con sh [ ](paréntesis individuales). Además de eso funciona muy bien, gracias!
Steffen Schwigon
39

Esta es una versión más elaborada de la versión de @ Brian:

Para cambiar el autor y el confirmador, puede hacer esto (con saltos de línea en la cadena que es posible en 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

Puede obtener uno de estos errores:

  1. El directorio temporal ya existe
  2. Las referencias que comienzan con refs / original ya existen
    (esto significa que otra rama de filtro se ha ejecutado previamente en el repositorio y la referencia de la rama original se respalda en refs / original )

Si desea forzar la ejecución a pesar de estos errores, agregue la --forcebandera:

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

Es -- --allposible que se necesite una pequeña explicación de la opción: hace que la rama de filtro funcione en todas las revisiones en todas las referencias (que incluye todas las ramas). Esto significa, por ejemplo, que las etiquetas también se reescriben y es visible en las ramas reescritas.

Un "error" común es usar HEADen su lugar, lo que significa filtrar todas las revisiones solo en la rama actual . Y luego no existirían etiquetas (u otras referencias) en la rama reescrita.

stigkj
fuente
Felicitaciones por proporcionar un procedimiento que cambia los commits en todas las referencias / sucursales.
Johnny Utahh
25

Un solo comando para cambiar el autor de las últimas N confirmaciones:

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

NOTAS

  • la --no-editbandera se asegura de git commit --amendque no pida una confirmación adicional
  • cuando lo use git rebase -i, puede seleccionar manualmente los commits donde cambiar el autor,

el archivo que edite se verá así:

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

Aún puede modificar algunas líneas para ver dónde desea cambiar el autor. Esto le da un buen punto medio entre la automatización y el control: ve los pasos que se ejecutarán, y una vez que guarde, todo se aplicará a la vez.

Chris Maes
fuente
¡Increíble! ¡Gracias!
Pablo Lalloni
Usé HEAD ~ 8 y muestra mucho más que los últimos 8 commits.
Bryan Bryce
1
@BryanBryce si hay compromisos de fusión involucrados, las cosas se complican :)
Chris Maes
@ ChrisMaes Ah, ya veo lo que está pasando. No quiero meterme con eso, solo en la rama en la que estoy.
Bryan Bryce
En ese caso, supongo que se ramificó del maestro, podría:git rebase -i master -x ...
Chris Maes
23
  1. correr git rebase -i <sha1 or ref of starting point>
  2. marque todas las confirmaciones que desea cambiar con edit(o e)
  3. repita los dos comandos siguientes hasta que haya procesado todas las confirmaciones:

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

Esto mantendrá toda la otra información de confirmación (incluidas las fechas). La --reuse-message=HEADopción evita que se inicie el editor de mensajes.

sporsh
fuente
23

Utilizo lo siguiente para reescribir al autor para un repositorio completo, incluidas las etiquetas y todas las ramas:

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

Luego, como se describe en la página MAN de filter-branch , elimine todas las referencias originales respaldadas por filter-branch(esto es destructivo, primero haga una copia de respaldo):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Ton van den Heuvel
fuente
2
Es muy importante usarlo --tag-name-filter cat. De lo contrario, sus etiquetas permanecerán en la cadena original de confirmaciones. Las otras respuestas no mencionan esto.
jeberle
21

Adapte esta solución que funciona ingiriendo un simple author-conv-file(el formato es el mismo que para git-cvsimport ). Funciona cambiando todos los usuarios como se define en elauthor-conv-file todas las ramas.

Usamos esto junto con cvs2git para migrar nuestro repositorio de cvs a git.

es decir, muestra author-conv-file

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

La secuencia de comandos:

 #!/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
fuente
Gracias, me pregunto por qué esta no es la funcionalidad central de git (o git-svn). Esto se puede hacer con una bandera para git svn clone, pero no en git filter-branch ...
Daniel Hershcovich
20

Debo señalar que si el único problema es que el autor / correo electrónico es diferente al habitual, esto no es un problema. La solución correcta es crear un archivo llamado .mailmapen la base del directorio con líneas como

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

Y a partir de entonces, los comandos like git shortlogconsiderarán esos dos nombres como iguales (a menos que usted les indique específicamente que no lo hagan). Ver http://schacon.github.com/git/git-shortlog.html para obtener más información.

Esto tiene la ventaja de todas las otras soluciones aquí, ya que no tiene que volver a escribir el historial, lo que puede causar problemas si tiene un flujo ascendente, y siempre es una buena manera de perder datos accidentalmente.

Por supuesto, si cometió algo como usted mismo y realmente debería ser otra persona, y no le importa reescribir la historia en este momento, cambiar el autor de confirmación es probablemente una buena idea para fines de atribución (en cuyo caso lo dirijo a mi Otra respuesta aquí).

asmeurer
fuente
18

Encontré que las versiones presentadas son demasiado agresivas, especialmente si confirma parches de otros desarrolladores, esto esencialmente robará su código.

La siguiente versión funciona en todas las ramas y cambia el autor y el comitter por separado para evitarlo.

Felicitaciones a leif81 para la opción de todos.

#!/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
fuente
18
  1. Cambie commit author name & emailpor Amendy luego reemplace 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. Otra forma 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
fuente
2
Muy buena respuesta. Me gusta que los cambios se envuelvan desde la misma actualización para incluso limpiar los git commits
Aleks
12

La forma más rápida y fácil de hacer esto es usar el argumento --exec de git rebase:

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

Esto creará una lista de tareas que se verá así:

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
...

y esto funcionará todo automáticamente, lo que funciona cuando tienes cientos de confirmaciones.

Lie Ryan
fuente
9

Si usted es el único usuario de este repositorio, puede reescribir la historia utilizando git filter-branch(como svick escribió ), o git fast-export/ git fast-importmás el guión de filtro (como se describe en el artículo referido en docgnome respuesta ) o interactiva de rebase . Pero cualquiera de ellos cambiaría las revisiones desde el primer compromiso modificado en adelante; Esto significa problemas para cualquiera que haya basado sus cambios en la reescritura de su rama.

RECUPERACIÓN

Si otros desarrolladores no basaron su trabajo en la versión previa a la reescritura, la solución más simple sería volver a clonar (clonar nuevamente).

Alternativamente, pueden intentarlo git rebase --pull, lo que avanzaría rápidamente si no hubiera cambios en su repositorio, o cambiaría su rama por encima de las confirmaciones reescritas (queremos evitar la fusión, ya que mantendría los comités de reescritura para siempre). Todo esto asumiendo que no tienen trabajo comprometido; use git stashpara guardar los cambios de otra manera.

Si otros desarrolladores usan ramas de características, y / o git pull --rebaseno funciona, por ejemplo, porque el flujo ascendente no está configurado, tienen que volver a basar su trabajo sobre los compromisos posteriores a la reescritura. Por ejemplo, justo después de buscar nuevos cambios ( git fetch), para una masterrama basada en / bifurcada origin/master, es necesario ejecutar

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

Aquí origin/master@{1}está el estado previo a la reescritura (antes de buscar), vea gitrevisions .


La solución alternativa sería usar refs / replace / mecanismo, disponible en Git desde la versión 1.6.5. En esta solución, proporciona reemplazos para confirmaciones que tienen correo electrónico incorrecto; entonces, cualquiera que fetch = +refs/replace/*:refs/replace/*busque referencias de 'reemplazo' (algo así como refspec en el lugar apropiado en su lugar .git/config) obtendría reemplazos de forma transparente, y aquellos que no busquen esas referencias verían compromisos antiguos.

El procedimiento es algo como esto:

  1. Encuentre todas las confirmaciones con correo electrónico incorrecto, por ejemplo, utilizando

    $ git log [email protected] --all
    
  2. Para cada confirmación incorrecta, cree una confirmación de reemplazo y agréguela a la base de datos de objetos

    $ 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. Ahora que ha corregido el compromiso en la base de datos de objetos, debe decirle a git que reemplace de forma automática y transparente el compromiso incorrecto por uno corregido mediante el git replacecomando:

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Finalmente, enumere todos los reemplazos para verificar si este procedimiento tuvo éxito

    $ git replace -l
    

    y verifique si se realizan reemplazos

    $ git log [email protected] --all
    

Por supuesto, puede automatizar este procedimiento ... bueno, todo excepto usar el git replaceque no tiene (todavía) modo por lotes, por lo que tendría que usar el bucle de shell para eso o reemplazarlo "a mano".

¡NO PROBADO! YMMV.

Tenga en cuenta que puede encontrar algunas esquinas ásperas al usar el refs/replace/mecanismo: es nuevo y aún no está muy bien probado .

Jakub Narębski
fuente
6

Si las confirmaciones que desea corregir son las últimas, y solo un par de ellas, puede usar una combinación de git resetygit stash volver a confirmarlas después de configurar el nombre y el correo electrónico correctos.

La secuencia será algo así (para 2 confirmaciones incorrectas, sin cambios pendientes):

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
fuente
5

Si está utilizando Eclipse con EGit, entonces hay una solución bastante fácil.
Suposición: tiene confirmaciones en una rama local 'local_master_user_x' que no se puede enviar a una rama remota 'maestra' debido al usuario no válido.

  1. Verificación de la rama remota 'maestro'
  2. Seleccione los proyectos / carpetas / archivos para los cuales 'local_master_user_x' contiene cambios
  3. Haga clic derecho - Reemplazar con - Rama - 'local_master_user_x'
  4. Confirme estos cambios nuevamente, esta vez como el usuario correcto y en la rama local 'maestro'
  5. Empuje al 'maestro' remoto
paphko
fuente
5

Con el rebase interactivo, puede colocar un comando de modificación después de cada confirmación que desee modificar. Por ejemplo:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
j16r
fuente
3
El problema con esto es que también se modifican otros metadatos de confirmación (por ejemplo, fecha y hora). Me acabo de enterar de la manera difícil ;-).
halfer
5

Tenga en cuenta que git almacena dos direcciones de correo electrónico diferentes, una para el emisor (la persona que cometió el cambio) y otra para el autor (la persona que escribió el cambio).

La información de confirmación no se muestra en la mayoría de los lugares, pero puede verla con git log -1 --format=%cn,%ce(o usarla en showlugar de logespecificar una confirmación particular).

Si bien cambiar el autor de tu último commit es tan simple como git commit --amend --author "Author Name <[email protected]>" , no hay una sola línea o argumento para hacer lo mismo con la información del committer.

La solución es (temporalmente o no) cambiar su información de usuario, luego enmendar el commit, lo que actualizará el committer a su información actual:

git config user.email [email protected] 
git commit --amend
Sir Athos
fuente
Tenga en cuenta que el valor anterior todavía está en algunos lugares path\to\repo\.git. Todavía no estoy seguro de qué necesitarías hacer para eliminarlo por completo. Desafortunadamente, las enmiendas (?) No parecen borrarse.
ruffin
5

Hoy hemos tenido un problema en el que un carácter UTF8 en el nombre de un autor causaba problemas en el servidor de compilación, por lo que tuvimos que reescribir el historial para corregirlo. Los pasos dados fueron:

Paso 1: cambie su nombre de usuario en git para todas las confirmaciones futuras, según las instrucciones aquí: https://help.github.com/articles/setting-your-username-in-git/

Paso 2: Ejecute el siguiente script bash:

#!/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

Descripción general rápida: Revise su repositorio en un archivo temporal, revise todas las ramas remotas, ejecute el script que reescribirá el historial, hará un impulso forzado del nuevo estado y les dirá a todos sus colegas que hagan un cambio de base para obtener los cambios.

Tuvimos problemas para ejecutar esto en OS X porque de alguna manera estropeó las terminaciones de línea en los mensajes de confirmación, por lo que tuvimos que volver a ejecutarlo en una máquina Linux después.

Miloš Ranđelović
fuente
5

Tu problema es muy común. Consulte " Uso de Mailmap para corregir la lista de autores en Git "

En aras de la simplicidad, he creado un script para facilitar el proceso: git-changemail

Después de poner ese script en su camino, puede emitir comandos como:

  • Cambiar coincidencias de autor en la rama actual

    $ git changemail -a [email protected] -n newname -m [email protected]
    
  • Cambie las coincidencias de autor y confirmador en <branch> y <branch2>. Pase -fa rama de filtro para permitir reescribir copias de seguridad

    $ git changemail -b [email protected] -n newname -m [email protected] -- -f &lt;branch> &lt;branch2>
    
  • Mostrar usuarios existentes en repositorio

    $ git changemail --show-both
    

Por cierto, después de realizar los cambios, limpie la copia de seguridad de la rama de filtro con: git-backup-clean

albfan
fuente
1
cuando ejecuto su comando, dice "fatal: no se puede ejecutar 'git-changemail': permiso denegado"
Govind
3

Quiero agregar mi ejemplo también. Quiero crear una bash_function con el parámetro dado.

esto funciona en 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
fuente
2

Si usted es el único usuario de este repositorio o no le importa posiblemente romper el repositorio para otros usuarios, entonces sí. Si ha empujado estas confirmaciones y existen en otro lugar donde puede acceder a ellas, entonces no, a menos que no le importe romper los repositorios de otras personas. El problema es que al cambiar estas confirmaciones, generará nuevos SHA que harán que se traten como confirmaciones diferentes. Cuando alguien más intenta obtener estas confirmaciones modificadas, la historia es diferente y kaboom.

Esta página http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html describe cómo hacerlo. (No he probado esto, así que YMMV)

baudtack
fuente
Por lo tanto, no hay una forma segura de reescribir el correo electrónico del usuario. Sin hacer estallar a todos los demás. Sabía que reescribir el historial era una mala idea, solo pensé que podría haber una manera limpia de hacerlo de manera segura. Gracias.
manumoomoo
@mediaslave: prueba el refs/replace/mecanismo.
Jakub Narębski
meta.stackexchange.com/a/8259/184684 - aka, suma enlaces para convertirlos en respuestas.
ruffin