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?

Flávio Amieiro
fonte
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="[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
fonte
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 protected]>" 

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 protected]>" --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 protected]>"

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.

asmeurer
fonte
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
Rognon
fonte
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
Brian Gianforcaro
fonte
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 [email protected]
git commit --amend --reset-author

testado com a versão 1.7.5.4 do git

lrkwz
fonte
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 protected]>"

(extraído da resposta do asmeurer)

azulado
fonte
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 protected]>" -C HEAD
pick 1234 my bad commit 2
exec git commit --amend --author="New Author Name <[email protected]>" -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.

Alex Brown
fonte
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" = "[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
fonte
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 <[email protected]>(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
svick
fonte
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

Ryanmt
fonte
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 "[email protected]" "[email protected]" 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.

brauliobo
fonte
"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.

stigkj
fonte
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 <[email protected]>' --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 <[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

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.

Chris Maes
fonte
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 <[email protected]>" ; 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.

sporsh
fonte
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
Ton van den Heuvel
fonte
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 <[email protected]>
jill=Jill Doe <[email protected]>

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
Leif Gruenwoldt
fonte
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).

asmeurer
fonte
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="<[email protected]>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
    GIT_COMMITTER_NAME="<new commiter>";
    GIT_COMMITTER_EMAIL="<[email protected]>";
fi
' -- --all
drahnr
fonte
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 <[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. 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 <[email protected]>"  # change the author name & email
    
    # Save changes and exit the editor
    
    $ git rebase --continue                # finish the rebase
    
Sajib Khan
fonte
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.

Lie Ryan
fonte
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 [email protected] --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/[email protected]/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 [email protected] --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 .

Jakub Narębski
fonte
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
djromero
fonte
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
paphko
fonte
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
j16r
fonte
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 protected]>", 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 [email protected] 
git commit --amend
Sir Athos
fonte
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="[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

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.

Miloš Ranđelović
fonte
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 [email protected] -n newname -m [email protected]
    
  • 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 [email protected] -n newname -m [email protected] -- -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

albfan
fonte
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:-"[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
fonte
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)

baudtack
fonte
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