Como alterar o nome do autor e do commit e o email de vários commit no Git?


2392

Eu estava escrevendo um script simples no computador da escola e cometendo as alterações no Git (em um repositório que estava no meu pendrive, clonado do meu computador em casa). Após várias confirmações, percebi que estava comprometendo coisas como usuário root.

Existe alguma maneira de mudar o autor desses commits para o meu nome?


13
Pergunta: o uso de git filter-branch preserva os SHA1 para tags, versões e objetos anteriores? Ou alterar a força do nome do autor também mudará os SHA1 associados?
andyl

36
Os hashes mudarão sim
Não disponível

3
Tangencialmente, criei um pequeno script que finalmente corrigiu a causa raiz para mim. gist.github.com/tripleee/16767aa4137706fd896c
tripleee

2
@impinball A idade da pergunta não é relevante. Criar uma nova pergunta duplicada está fora de questão. Suponho que poderia criar uma pergunta que implore por essa resposta em particular, mas não estou totalmente convencido de que obteria tanta visibilidade. Não é como se houvesse uma escassez de perguntas sobre o Git aqui ... Ainda bem que eu poderia ajudar.
Tripleee

8
GitHub tem roteiro especial para isso: help.github.com/articles/changing-author-info
Timur Bernikovich

Respostas:


1214

Esta resposta é utilizada git-filter-branch, para a qual os documentos agora fornecem este aviso:

O git filter-branch tem uma infinidade de armadilhas que podem produzir manipulações não óbvias da reescrita pretendida do histórico (e podem deixar pouco tempo para investigar esses problemas, uma vez que ele tem um desempenho tão abismal). Esses problemas de segurança e desempenho não podem ser corrigidos de forma compatível com versões anteriores e, como tal, seu uso não é recomendado. Por favor, use uma ferramenta alternativa de filtragem de histórico, como o git filter-repo . Se você ainda precisar usar o git filter-branch, leia com atenção SEGURANÇA (e DESEMPENHO ) para aprender sobre as minas terrestres do filtro-branch e, em seguida, evite vigilantemente o maior número possível de perigos listados lá.

Alterar o autor (ou responsável) exigiria reescrever toda a história. Se você concorda com isso e acha que vale a pena, deve verificar o git filter-branch . A página do manual inclui vários exemplos para você começar. Observe também que você pode usar variáveis ​​de ambiente para alterar o nome do autor, confirmador, datas etc. - consulte a seção "Variáveis ​​de ambiente" da página de manual do git .

Especificamente, você pode corrigir todos os nomes e e-mails de autores errados para todos os ramos e tags com este comando (fonte: ajuda do GitHub ):

#!/bin/sh

git filter-branch --env-filter '
OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"
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

612
O Github tem um script público para esse help.github.com/articles/changing-author-info e funciona muito bem!
defvol

34
Após executar o script, você pode remover a ramificação de backup executando "git update-ref -d refs / original / refs / heads / master".
DR

7
@ rodowi, duplica todos os meus commits.
Rafael Barros

6
@RafaelBarros as informações do autor (como qualquer outra coisa na história) fazem parte da chave sha do commit. Qualquer alteração no histórico é uma reescrita que leva a novos IDs para todos os commit. Portanto, não reescrever em um repo compartilhado ou certifique-se todos os usuários estão cientes disso ...
Johannes

20
Resolvido usandogit push --force --tags origin HEAD:master
mcont

1577

NOTA: Essa resposta altera os SHA1s, portanto, use-o em uma ramificação que já foi enviada por push. Se você deseja apenas corrigir a ortografia de um nome ou atualizar um email antigo, o git permite fazer isso sem reescrever o histórico usando .mailmap. Veja minha outra resposta .

Usando o rebase interativo

Você poderia fazer

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

Em seguida, marque todos os seus commits incorretos como "editar" no arquivo rebase. Se você também deseja alterar seu primeiro commit, você deve adicioná-lo manualmente como primeira linha no arquivo rebase (siga o formato das outras linhas). Então, quando o git solicitar que você altere cada commit, faça

 git commit --amend --author "New Author Name <email@address.com>" 

edite ou feche o editor que é aberto e faça

git rebase --continue

para continuar a rebase.

Você pode pular a abertura do editor aqui, acrescentando --no-edit para que o comando seja:

git commit --amend --author "New Author Name <email@address.com>" --no-edit && \
git rebase --continue

Confirmação única

Como alguns comentadores observaram, se você deseja alterar apenas a confirmação mais recente, o comando rebase não é necessário. Apenas faça

 git commit --amend --author "New Author Name <email@address.com>"

Isso mudará o autor para o nome especificado, mas o commit será definido para o usuário configurado em git config user.namee git config user.email. Se você deseja definir o committer para algo que você especificar, isso definirá o autor e o committer:

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

Nota sobre Mesclar confirmações

Houve uma pequena falha na minha resposta original. Se houver algum commit de mesclagem entre o atual HEADe o seu <some HEAD before all your bad commits>, então git rebaseos achatará (e, a propósito, se você usar solicitações pull do GitHub, haverá uma tonelada de commits de mesclagem no seu histórico). Muitas vezes, isso pode levar a um histórico muito diferente (como alterações duplicadas podem ser "rebatizadas") e, na pior das hipóteses, pode git rebasesolicitar que você resolva conflitos difíceis de mesclagem (que provavelmente já foram resolvidos nos commits de mesclagem). A solução é usar o -psinalizador para git rebase, que preservará a estrutura de mesclagem do seu histórico. A página de manual git rebasealerta que o uso -pe -ipode levar a problemas, mas noBUGS seção que diz "Editar confirmações e reformular suas mensagens de confirmação devem funcionar bem".

Eu adicionei -pao comando acima. No caso em que você está apenas alterando a confirmação mais recente, isso não é um problema.


27
Excelente para o estranho cometer embora - útil se você estiver emparelhamento e se esqueça de mudar o autor
mloughran

32
1 para mencionar o caso de uso para a correção típica de um erro: git commit --amend --author = nome de usuário
Nathan Kidd

12
Isso é perfeito, o meu caso mais comum é que eu me sento em outro computador e esqueço de configurar o autor e, portanto, geralmente tenho <5 commits ou mais para corrigir.
Zitrax 21/08/10

57
git commit --amend --reset-authortambém funciona uma vez user.namee user.emailestá configurado corretamente.
pts

14
Reescreva as informações do autor em todas as confirmações depois de <commit>usar user.namee user.emailde ~/.gitconfig: executar git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', salvar, sair. Não há necessidade de editar!
Ntc2 6/03/2015

588

Você também pode fazer:

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

Observe que, se você estiver usando este comando no prompt de comando do Windows, precisará usar em "vez 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

4
Usar o env-filter não é a solução mais fácil? Não tenho certeza por que isso está recebendo mais votos, então.
stigkj

3
Então o link está quebrado. Como enviamos essas alterações para outro repositório?
1811 Russell Russell

28
env-filter mudará todos os commits. Esta solução permite uma condicional.
precisa saber é o seguinte

5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"desculpe, mas onde o -fsinalizador estará quando executar este script duas vezes. Na verdade, isso está na resposta de Brian, desculpe-me pela perturbação logo após a ramificação do filtro ser a solução.
hhh

2
@ user208769 o env-filter também permite uma condicional; olhada na minha resposta :-)
stigkj

559

Um liner, mas tenha cuidado se você tiver um repositório multiusuário - isso mudará todas as confirmações para ter o mesmo (novo) autor e 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

Com quebras de linha na string (o que é possível no 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

Ponto secundário, a exportação é realmente supérflua, embora não cause danos. por exemplo, git-filter-branch --env-filter "GIT_AUTHOR_NAME = 'Novo nome'; GIT_AUTHOR_EMAIL = 'Novo email'" HEAD.
Alec the Geek

4
Por que reescreve todas as confirmações se você especificar HEADno final do comando?
precisa

1
Isso não funciona para o meu repositório bitbucket, alguma ideia? Eu faço um git push --force --tags origin 'refs/heads/*'após o comando recomendado
Olorin 05/10

1
O comando push para isso é:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK

1
Arrumado; isso mantém os timestamps antigos também.
DharmaTurtle 6/03

221

Isso acontece quando você não possui um $ HOME / .gitconfig inicializado. Você pode corrigir isso como:

git config --global user.name "you name"
git config --global user.email you@domain.com
git commit --amend --reset-author

testado com a versão 1.7.5.4 do git


9
Isso funciona muito bem no último commit. Agradável e simples. Não tem de haver uma mudança global, usando --localobras também
Ben

Este foi o grande vencedor para mim! O git commit --amend --reset-author --no-editcomando é especialmente útil se você criou confirmações com as informações incorretas do autor e, em seguida, defina o autor correto após git config. Salvei meu a $$ agora mesmo quando tive que atualizar meu e-mail.
187 ecbrodie

187

Para um único commit:

git commit --amend --author="Author Name <email@address.com>"

(extraído da resposta do asmeurer)


14
mas isso é apenas se ele é o mais recente comprometer
Richard

4
De acordo com git help commit, git commit --amendaltera o commit na "ponta do ramo atual" (que é HEAD). Normalmente, esse é o commit mais recente, mas você pode fazer o commit que desejar, primeiro verificando o commit com git checkout <branch-name>ou git checkout <commit-SHA>.
Rory O'Kane

12
Mas se você fizer isso, todas as confirmações que já possuem essa confirmação como pai apontarão para a confirmação incorreta. Melhor usar a ramificação do filtro nesse ponto.
John Gietzen

3
@ JohnGietzen: Você pode recuperar os commits de volta para o que foi alterado para corrigir isso. No entanto, se você estiver executando> 1 commit, como mencionado, o filtro-branch provavelmente será muito mais fácil.
Thanatos

5
Observe que essas alterações apenas confirmam authore não ocommitter
Nick Volynkin

179

No caso em que apenas os poucos commits superiores possuem autores inválidos, você pode fazer isso por dentro git rebase -iusando o execcomando e o --amendcommit, da seguinte maneira:

git rebase -i HEAD~6 # as required

que apresenta a lista editável de confirmações:

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

Em seguida, adicione exec ... --author="..."linhas após todas as linhas com autores inválidos:

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

salve e saia do editor (para executar).

Essa solução pode demorar mais tempo do que algumas outras, mas é altamente controlável - eu sei exatamente o que confirma.

Obrigado a @asmeurer pela inspiração.


26
Definitivamente incrível. Você pode reduzi-lo definindo user.name e user.email na configuração local do repositório e, em seguida, cada linha é apenas exec git commit --amend --reset-author -C HEAD?
30812 Andrew

1
A resposta canônica, para usar o branch-filter, apenas excluiu refs / heads / master para mim. Então, marque com +1 sua solução editável e controlável. Obrigado!
jmtd

Por que você começa em Someone else's commitvez de my bad commit 1? Eu apenas tentei HEAD^^alterar os 2 últimos commits e funcionou perfeitamente.
Fredoverflow

3
No lugar de git rebase -i HEAD^^^^^^você também pode escrevergit rebase -i HEAD~6
Patrick Schlüter

1
Observe que isso altera o carimbo de data / hora das confirmações. Consulte stackoverflow.com/a/11179245/1353267 para reverter para os registros de data
Samveen 10/10

111

O Github tem uma boa solução , que é o seguinte 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" = "your@email.to.match" ]
then
    cn="Your New Committer Name"
    cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "your@email.to.match" ]
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"
'

5
Funcionou perfeitamente. Só precisei git reset --hard HEAD^algumas vezes nos outros repositórios locais para obtê-los para uma versão anterior, git pulla versão alterada, e aqui estou eu sem linhas que contenham unknown <stupid-windows-user@.StupidWindowsDomain.local>(adorei o padrão do git).
Alan Plum

1
Eu não posso empurrar depois disso. Eu tenho que usar "-f"?
Fish Monitor

9
Eu fiz git push -f. Além disso, os repositórios locais precisam ser recolocados depois disso.
Fish Monitor

Se você precisar executar o shell script em uma ramificação específica, poderá alterar a última linha para: "'master..your-branch-name" (assumindo que você ramificou a master).
Robert Kajic

Clique no link <solução agradável> como o roteiro foi atualizado
gxpr

82

Como o documento mencionou, reescrever o histórico é perigoso e quebrará os repositórios de outras pessoas.

Mas se você realmente deseja fazer isso e está em um ambiente bash (não há problema no Linux, no Windows, você pode usar o git bash, fornecido com a instalação do git), use o 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 as coisas, você pode especificar uma série de revisões que deseja reescrever:

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

2
Observe que isso deixará as tags apontando para os commits antigos. --tag-name-filter caté a opção "faça funcionar".
Roman Starkov 27/03

@romkyns alguma idéia de como alterar tags também?
Nick Volynkin

@NickVolynkin Sim, você especifica --tag-name-filter cat. Este realmente deveria ter sido o comportamento padrão.
Roman Starkov

48

Ao assumir um commit não imerso de outro autor, existe uma maneira fácil de lidar com isso.

git commit --amend --reset-author


1
Para um único commit, e se você quiser colocar seu nome de usuário, é a maneira mais fácil.
Pedro Benevides

7
Você pode adicionar --no-editpara fazer isso ainda mais fácil, como geralmente a maioria das pessoas vai querer atualizar somente o endereço de email e não a mensagem de commit
PlagueHammer

Vocês podem, por favor, compartilhar o comando git apenas para atualizar o email / nome de usuário do último commit com o novo?
adi

Você tentou isso? Isso deve ser um efeito colateral disso, se não o stackoverflow.com/a/2717477/654245 parecer um bom caminho.
Ryanmt

47

Você pode usar isso como um alias para poder:

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

ou para os últimos 10 confirmados:

git change-commits GIT_AUTHOR_EMAIL "old@email.com" "new@email.com" HEAD~10..HEAD

Adicione 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 "

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

Espero que seja útil.


"git: 'change-commits' não é um comando git. Veja 'git --help'."
Native_Mobile_Arch_Dev

Após este comando e sincronização com o mestre, todas as confirmações no histórico são duplicadas! Mesmo de outros usuários :(
Vladimir

@Vladimir que é esperado, por favor estudar sobre mudar a história em git
brauliobo

Para mim, parece ser executado em / bin / sh, então tive que substituir o teste específico do bash pelo teste [[ ]]compatível com sh [ ](colchetes simples). Além disso, funciona muito bem, obrigado!
Steffen Schwigon

39

Esta é uma versão mais elaborada da versão do @ Brian:

Para alterar o autor e o commit, você pode fazer isso (com quebras de linha na string, que é possível no 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

Você pode receber um destes erros:

  1. O diretório temporário já existe
  2. As referências que começam com refs / original já existem
    (isso significa que outra ramificação do filtro foi executada anteriormente no repositório e a referência da ramificação original foi copiada em refs / original )

Se você deseja forçar a execução apesar desses erros, adicione o --forcesinalizador:

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

-- --allPode ser necessária uma pequena explicação da opção: Faz com que a ramificação do filtro funcione em todas as revisões em todas as refs (o que inclui todas as ramificações). Isso significa, por exemplo, que as tags também são reescritas e são visíveis nas ramificações reescritas.

Um "erro" comum é o de usar HEAD, o que significa filtrar todas as revisões apenas na ramificação atual . E, em seguida, nenhuma tag (ou outras referências) existiria na ramificação reescrita.


Parabéns por fornecer um procedimento que altera confirmações em todas as refs / branches.
Johnny Utahh

25

Um único comando para alterar o autor para o último N confirma:

git rebase -i HEAD~4 -x "git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit"

NOTAS

  • a --no-editbandeira garante que git commit --amendnão solicite uma confirmação extra
  • Quando você usa git rebase -i, você pode selecionar manualmente os commits onde alterar o autor,

o arquivo que você editar ficará assim:

pick 897fe9e simplify code a little
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
pick dc18f70 bugfix
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit

Você ainda pode modificar algumas linhas para ver onde deseja alterar o autor. Isso fornece um bom meio termo entre automação e controle: você vê as etapas que serão executadas e, depois de salvar, tudo será aplicado de uma só vez.


Impressionante! Obrigado!
Pablo Lalloni 30/08/19

Eu usei HEAD ~ 8 e mostra muito mais do que os últimos 8 commits.
Bryan Bryce

1
@BryanBryce se houver comprometimento de mesclagem envolvido, as coisas ficam complicadas :)
Chris Maes

@ ChrisMaes Ah, eu vejo o que está acontecendo. Não quero mexer com isso, apenas no ramo em que estou.
Bryan Bryce

Nesse caso, supondo que você se ramificou do mestre, você poderia:git rebase -i master -x ...
Chris Maes

23
  1. corre git rebase -i <sha1 or ref of starting point>
  2. marque todas as confirmações com as quais deseja alterar edit(ou e)
  3. faça o loop dos dois comandos a seguir até processar todas as confirmações:

    git commit --amend --reuse-message=HEAD --author="New Author <new@author.email>" ; git rebase --continue

Isso manterá todas as outras informações de confirmação (incluindo as datas). A --reuse-message=HEADopção impede que o editor de mensagens seja iniciado.


23

Eu uso o seguinte para reescrever o autor para um repositório inteiro, incluindo tags e todas as ramificações:

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

Em seguida, conforme descrito na página MAN da ramificação de filtro , remova todas as refs originais com backup filter-branch(isso é destrutivo, faça backup primeiro):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d

2
É muito importante usar --tag-name-filter cat. Caso contrário, suas tags permanecerão na cadeia de confirmações original. As outras respostas não mencionam isso.
Jeberle

21

Eu adaptei esta solução que funciona ingerindo um simples author-conv-file(o formato é o mesmo do git-cvsimport ). Ele funciona alterando todos os usuários, conforme definido em author-conv-filetodas as filiais.

Usamos isso em conjunto com cvs2gitpara migrar nosso repositório do cvs para o git.

ou seja, amostra author-conv-file

john=John Doe <john.doe@hotmail.com>
jill=Jill Doe <jill.doe@hotmail.com>

O script:

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

Obrigado, gostaria de saber por que essa não é a funcionalidade principal do git (ou git-svn). Isso pode ser feito com uma bandeira para o clone svn git, mas não em git filter-branch ...
Daniel Hershcovich

20

Devo salientar que, se o único problema é que o autor / email é diferente do habitual, isso não é um problema. A correção correta é criar um arquivo chamado .mailmapna base do diretório com linhas como

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

E a partir de então, comandos como git shortlogconsiderarão esses dois nomes iguais (a menos que você diga especificamente a eles). Consulte http://schacon.github.com/git/git-shortlog.html para obter mais informações.

Isso tem a vantagem de todas as outras soluções aqui, pois você não precisa reescrever o histórico, o que pode causar problemas se você tiver um upstream e é sempre uma boa maneira de perder dados acidentalmente.

É claro que, se você cometeu algo como você mesmo e deveria realmente ser outra pessoa, e não se importa em reescrever o histórico neste momento, alterar o autor do commit provavelmente é uma boa idéia para fins de atribuição (nesse caso, eu o direciono para o meu outra resposta aqui).


18

Eu achei as versões apresentadas muito agressivas, especialmente se você cometer patches de outros desenvolvedores, isso essencialmente roubará seu código.

A versão abaixo funciona em todas as ramificações e altera o autor e o emissor separadamente para evitar isso.

Parabéns ao leif81 pela opção all.

#!/bin/bash

git filter-branch --env-filter '
if [ "$GIT_AUTHOR_NAME" = "<old author>" ];
then
    GIT_AUTHOR_NAME="<new author>";
    GIT_AUTHOR_EMAIL="<youmail@somehost.ext>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
    GIT_COMMITTER_NAME="<new commiter>";
    GIT_COMMITTER_EMAIL="<youmail@somehost.ext>";
fi
' -- --all

18
  1. Altere commit author name & emailpor Amend, substituindo old-commit with new-one:

    $ git checkout <commit-hash>                            # checkout to the commit need to modify  
    $ git commit --amend --author "name <author@email.com>" # 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. Outra maneira 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 <author@email.com>"  # change the author name & email
    
    # Save changes and exit the editor
    
    $ git rebase --continue                # finish the rebase
    

2
Resposta muito boa. Eu gosto que as alterações são embrulhado desde o update até mesmo limpar os commits git
Aleks

12

A maneira mais rápida e fácil de fazer isso é usar o argumento --exec do git rebase:

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

Isso criará uma lista de tarefas assim:

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

e isso funcionará automaticamente, o que funciona quando você tem centenas de confirmações.


9

Se você é o único usuário deste repositório, é possível reescrever o histórico usando git filter-branch(como svick escreveu ) ou o script de filtro git fast-export/ git fast-importplus (conforme descrito no artigo mencionado na resposta do docgnome ) ou o rebase interativo . Mas qualquer um desses alteraria as revisões a partir do primeiro commit alterado; isso significa problemas para qualquer pessoa que tenha baseado suas alterações na sua pré-reescrita.

RECUPERAÇÃO

Se outros desenvolvedores não basearem seu trabalho na versão pré-reescrita, a solução mais simples seria clonar novamente (clonar novamente).

Como alternativa, eles podem tentar git rebase --pull, o que aceleraria se não houvesse alterações em seu repositório ou refizesse sua ramificação sobre as confirmações reescritas (queremos evitar a mesclagem, pois isso manteria as reescrições pré-reescritas para sempre). Tudo isso supondo que eles não tenham comprometido o trabalho; use git stashpara esconder as alterações caso contrário.

Se outros desenvolvedores usam ramificações de recursos, e / ou git pull --rebasenão funcionam, por exemplo, porque o upstream não está configurado, eles precisam refazer o trabalho em cima das confirmações pós-reescrita. Por exemplo, logo após buscar novas alterações ( git fetch), para um masterramo baseado em / bifurcado de origin/master, é necessário executar

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

Aqui origin/master@{1}está o estado de pré-reescrita (antes da busca), consulte gitrevisions .


A solução alternativa seria usar refs / replace / mecanismo, disponível no Git desde a versão 1.6.5. Nesta solução, você fornece substituições para confirmações com email errado; então qualquer um que busca 'substituir' refs (algo como fetch = +refs/replace/*:refs/replace/*refspec no lugar apropriado em suas .git/config ) obteria substituições transparente e aqueles que não buscar esses refs veria commits idade.

O procedimento é mais ou menos assim:

  1. Encontre todas as confirmações com email errado, por exemplo, usando

    $ git log --author=user@wrong.email --all
    
  2. Para cada confirmação incorreta, crie uma confirmação de substituição e adicione-a ao banco de dados do objeto

    $ git cat-file -p <ID of wrong commit> | 
      sed -e 's/user@wrong\.email/user@example.com/g' > tmp.txt
    $ git hash-object -t commit -w tmp.txt
    <ID of corrected commit>
    
  3. Agora que você corrigiu o commit no banco de dados de objetos, é necessário dizer ao git para substituir automática e transparentemente o commit errado, corrigindo um usando o git replacecomando:

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Por fim, liste todas as substituições para verificar se este procedimento foi bem-sucedido

    $ git replace -l
    

    e verifique se as substituições ocorrem

    $ git log --author=user@wrong.email --all
    

É claro que você pode automatizar esse procedimento ... bem, todos, exceto o uso git replaceque ainda não possui o modo em lote, então você teria que usar o shell loop para isso ou substituir "manualmente".

NÃO TESTADO! YMMV.

Observe que você pode encontrar alguns ângulos difíceis ao usar o refs/replace/mecanismo: ele é novo e ainda não está muito bem testado .


6

Se os commits que você deseja corrigir são os mais recentes, e apenas alguns deles, você pode usar uma combinação git resete git stashvoltar atrás e confirmá-los novamente depois de configurar o nome e o email corretos.

A sequência será algo assim (para 2 confirmações incorretas, sem alterações pendentes):

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

5

Se você estiver usando o Eclipse com EGit, existe uma solução bastante fácil.
Suposição: você tem confirmações em uma ramificação local 'local_master_user_x' que não podem ser enviadas para uma ramificação remota 'master' devido ao usuário inválido.

  1. Fazer checkout da filial remota 'master'
  2. Selecione os projetos / pastas / arquivos para os quais 'local_master_user_x' contém alterações
  3. Clique com o botão direito do mouse - Substituir por - Ramificação - 'local_master_user_x'
  4. Confirme essas alterações novamente, desta vez como o usuário correto e na ramificação local 'master'
  5. Enviar para 'mestre' remoto

5

Usando o rebase interativo, você pode colocar um comando de correção após cada confirmação que deseja alterar. Por exemplo:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead

3
O problema é que outros metadados de confirmação (por exemplo, data e hora) também são alterados. Acabei de descobrir isso da maneira mais difícil ;-).
Halfer

5

Note-se que as lojas git dois endereços de e-mail diferentes, um para o committer (a pessoa que cometeu a mudança) e outra para o autor (a pessoa que escreveu a mudança).

As informações do committer não são exibidas na maioria dos lugares, mas você pode vê-las com git log -1 --format=%cn,%ce(ou usar em showvez de logespecificar um commit específico).

Embora alterar o autor do seu último commit seja tão simples quanto git commit --amend --author "Author Name <email@example.com>", não há argumento ou argumento único para fazer o mesmo com as informações do commit .

A solução é (temporariamente ou não) alterar as informações do usuário e, em seguida, alterar o commit, que atualizará o commit com as informações atuais:

git config user.email my_other_email@example.com 
git commit --amend

Observe que o valor antigo ainda está em alguns lugares em path\to\repo\.git. Ainda não tenho certeza do que você precisa fazer para eliminá-lo totalmente. Infelizmente, as alterações (?) Não parecem apagar.
Ruffin

5

Hoje enfrentamos um problema em que um caractere UTF8 em um nome de autor estava causando problemas no servidor de compilação; portanto, tivemos que reescrever o histórico para corrigir isso. Os passos dados foram:

Etapa 1: altere seu nome de usuário no git para todas as confirmações futuras, conforme instruções aqui: https://help.github.com/articles/setting-your-username-in-git/

Etapa 2: execute o seguinte 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="me@something.com"
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

Visão geral rápida: faça o check-out do seu repositório em um arquivo temporário, faça o check-out de todas as ramificações remotas, execute o script que irá reescrever o histórico, faça um push forçado do novo estado e peça a todos os seus colegas para fazer uma nova refazer para obter as alterações.

Tivemos problemas ao executar isso no OS X porque, de alguma forma, atrapalhava as terminações de linha nas mensagens de confirmação, então tivemos que executá-lo novamente em uma máquina Linux posteriormente.


5

Seu problema é realmente comum. Consulte " Usando o Mailmap para corrigir a lista de autores no Git "

Por uma questão de simplicidade, criei um script para facilitar o processo: git-changemail

Depois de colocar esse script em seu caminho, você pode emitir comandos como:

  • Alterar correspondências de autor na ramificação atual

    $ git changemail -a old@email.com -n newname -m new@email.com
    
  • Altere as correspondências de autor e commit em <ramificação> e <ramificação2>. Passe -fpara a ramificação do filtro para permitir a reescrita de backups

    $ git changemail -b old@email.com -n newname -m new@email.com -- -f &lt;branch> &lt;branch2>
    
  • Mostrar usuários existentes no repositório

    $ git changemail --show-both
    

A propósito, depois de fazer suas alterações, limpe o backup da ramificação do filtro com: git-backup-clean


1
quando executo seu comando, ele diz "fatal: não é possível executar o 'git-changemail': permissão negada"
Govind 2/15


3

Também quero adicionar meu exemplo. Eu quero criar uma função bash_ com determinado parâmetro.

isso funciona no 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:-"you_wrong_mail@hello.world"}
 NEW_NAME=${2:-"your name"}
 NEW_EMAIL=${3:-"new_mail@hello.world"}

 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;
}

2

Se você é o único usuário deste repositório ou não se preocupa em possivelmente quebrá-lo para outros usuários, sim. Se você enviou esses commits e eles existem onde outro lugar pode acessá-los, então não, a menos que você não se importe em quebrar os acordos de outras pessoas. O problema é que, alterando essas confirmações, você gerará novos SHAs que farão com que sejam tratados como confirmações diferentes. Quando alguém tenta obter esses commits alterados, a história é diferente e kaboom.

Esta página http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html descreve como fazê-lo. (Eu não tentei isso, então YMMV)


Portanto, não há uma maneira segura de reescrever o user.email. Sem explodir todo mundo. Eu sabia que reescrever a história era uma má ideia, apenas pensei que poderia haver uma maneira limpa de fazer isso com segurança. Obrigado.
Manumoomoo 04/08/10

@mediaslave: Tente refs/replace/mecanismo.
Jakub Narębski

meta.stackexchange.com/a/8259/184684 - também conhecido como soma de links para transformá-los em respostas.
Ruffin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.