Como alterar o nome do autor e do committer e E-mail de vários commits no Git?
30 answers
Mudar o autor (ou o committer) exigiria reescrever todo o histórico. Se não te importares com isso e achares que vale a pena, devias ir ver o git filter-branch. A página man inclui vários exemplos para começar. Observe também que você pode usar variáveis de ambiente para mudar o nome do autor, committer, datas, etc. -- ver a secção "variáveis de Ambiente" da Página do git man .
Especificamente, você pode corrigir todo o autor errado nomes e E E-mails para todas as ramificações e marcas 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
Usando Rebase Interactiva
Podias fazer
git rebase -i -p <some HEAD before all of your bad commits>
Depois marque todos os seus commits inválidos como "editar" no ficheiro de ajuste de base. Se você também quiser alterar o seu primeiro commit, você tem que adicioná-lo manualmente como primeira linha no arquivo rebase (siga o formato das outras linhas). Então, quando o git lhe pedir para emendar cada commit, faça
git commit --amend --author "New Author Name <[email protected]>"
Edite ou Apenas feche o editor que abre, e depois faça
git rebase --continue
Para continuar o ajuste.
Podes saltar. abrir o editor por completo aqui, adicionando--no-edit
para que o comando seja:
git commit --amend --author "New Author Name <[email protected]>" --no-edit && \
git rebase --continue
Compromisso Único
Como alguns dos comentaristas observaram, se você só quer mudar o commit mais recente, o comando rebase não é necessário. Apenas faça
git commit --amend --author "New Author Name <[email protected]>"
Isto irá mudar o autor para o nome indicado, mas o 'committer' será definido para o seu utilizador configurado em git config user.name
e git config user.email
. Se você quiser configurar o envio para algo que você especificar, isto irá definir tanto o autor e o committer:
git -c user.name="New Author Name" -c [email protected] commit --amend --reset-author
Nota no Merge Commits
Havia uma pequena falha na minha resposta original. Se houver algum commits merge entre o atualHEAD
e o seu <some HEAD before all your bad commits>
, então git rebase
irá achatá-los (e, a propósito, Se você usar os pedidos do GitHub pull, vai haver uma tonelada de commits merge no seu histórico). Isto pode muitas vezes levar a uma história muito diferente (como as mudanças duplicadas podem ser "rebased out"), e no pior caso, pode levar a git rebase
pedir-lhe para resolver conflitos de junção difÃceis (que provavelmente já foram resolvidos nos commits de junção). A solução é usar a bandeira -p
para git rebase
, que irá preservar a estrutura de junção da sua história. A página de manual de git rebase
avisa que o uso de -p
e -i
pode levar a problemas, mas na Secção BUGS
diz que "editar commits e reformular as suas mensagens de commit devem funcionar bem."
Adicionei -p
ao comando acima. Para o caso em que estás a mudar o mais recente commit, isto não é um problema.
Também podes 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
Nota, Se estiver a usar este comando na linha de comandos do Windows, então terá de 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
Um liner, mas tenha cuidado se tiver um repositório multi-utilizador - isto irá alterar todos se comprometem a ter o mesmo (novo) autor e committer.
git filter-branch -f --env-filter "GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='new@email'; GIT_COMMITTER_NAME='Newname'; GIT_COMMITTER_EMAIL='new@email';" HEAD
Com linhas na cadeia (o que é possÃvel na 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
git config --global user.name "you name"
git config --global user.email [email protected]
git commit --amend --reset-author
Testado com git versão 1.7.5.4
Para um único commit:
git commit --amend --author="Author Name <[email protected]>"
(extraÃdo da resposta de asmeur)
No caso em que apenas os poucos commits de topo têm maus autores, você pode fazer tudo isso dentro git rebase -i
usando o comando exec
e o commit --amend
, como se segue:
git rebase -i HEAD~6 # as required
Que lhe apresenta a lista editável de commits:
pick abcd Someone else's commit
pick defg my bad commit 1
pick 1234 my bad commit 2
Depois adicionar exec ... --author="..."
linhas depois de todas as linhas com maus 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
Gravar e sair do editor (para executar).
Esta solução pode ser mais longa para digitar do que algumas outras, mas é altamente controlável - eu sei exatamente o que a compromete bate.
Obrigado a @asmeurer pela inspiração.O Github tem uma solução Boa, que é o seguinte shell script:
#!/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"
'
Mas se realmente quiser fazer isso e estiver num ambiente bash (sem problemas no Linux, no Windows, poderá usar o Git bash, que é 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, poderá indicar 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
Ao assumir um commit não acumulado de outro autor, há uma maneira fácil de lidar com isso.
git commit --amend --reset-author
Esta é uma versão mais elaborada da versão de @Brian:
Para mudar o autor e o committer, você pode fazer isto (com as linhas na cadeia que é possÃvel na 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 ter um desses erros:
- a pasta temporária já existe
- Refs começando com refs / original já existe
(isto significa que outro ramo de filtragem foi executado anteriormente no repositório e o ramo original então referência: documento de referência ref / original)
Se quiser forçar a corrida apesar destes erros, adicione o --force
Pavilhão:
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
Pode ser necessária uma pequena explicação da Opção -- --all
: faz com que o ramo filtrante trabalhe em todas as revisões em todos os refs (que inclui todos os ramos). Isto significa, por exemplo, que as etiquetas também são reescritas e são visÃveis nos ramos reescritos.
Um "erro" comum é usar HEAD
em vez disso, o que significa filtrar todas as revisões apenas no ramo actual. E então nenhum tags (ou outros refs) existiria no ramo reescrito.
Pode usar este pseudónimo para poder fazer:
git change-commits GIT_AUTHOR_NAME "old name" "new name"
Ou para os últimos 10 commits:
git change-commits GIT_AUTHOR_EMAIL "[email protected]" "[email protected]" HEAD~10..HEAD
Também conhecido por:
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.- corre
git rebase -i <sha1 or ref of starting point>
- marque todos os commits que deseja alterar com
edit
(oue
) -
Repete os dois comandos seguintes até ter processado todos os commits:
git commit --amend --reuse-message=HEAD --author="New Author <[email protected]>"
;git rebase --continue
Isto manterá todas as outras informações de commit (incluindo as datas).
A opção --reuse-message=HEAD
impede que o editor de mensagens seja lançado.
Uso o seguinte para reescrever o autor para um repositório inteiro, incluindo marcas e todos os ramos:
git filter-branch --tag-name-filter cat --env-filter "
export GIT_AUTHOR_NAME='New name';
export GIT_AUTHOR_EMAIL='New email'
" -- --all
Então, como descrito na página de manual do filter-branch, Remova todos os refs originais suportados por filter-branch
(isto é destrutivo, backup primeiro):
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Adaptei esta solução que funciona ingerindo um simples author-conv-file
(o formato é o mesmo que um para git-cvsimport ). Ele funciona mudando todos os usuários conforme definido no author-conv-file
em todos os ramos.
Usámos isto em conjunto com cvs2git
para migrar o 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 guião:
#!/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
Eu achei as versões apresentadas de forma agressiva, especialmente se você commit patches de outros desenvolvedores, isso vai essencialmente roubar o código deles.
{[[2]} a versão abaixo funciona em todos os ramos e muda o autor e comentador separadamente para evitar isso. Parabéns para leif81 pela opção "tudo".#!/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
-
Mudar o commit
author name & email
porAmend
, em seguida substituindoold-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
-
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
Devo salientar que se o único problema é que o autor/e-mail é diferente do habitual, este não é um problema. A correção correta é criar um arquivo chamado .mailmap
na 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 daÃ, comandos como git shortlog
considerarão esses dois nomes iguais (a menos que lhes diga especificamente para não o fazerem). Ver http://schacon.github.com/git/git-shortlog.html para mais informações.
Se você for o único usuário do repositório, você pode reescrever a história usando git filter-branch
(como svick escreveu), ou git fast-export
/git fast-import
além de filtro de script (como descrito no artigo referenciado no docgnome resposta), ou interativa rebase. Mas qualquer um deles mudaria as revisões a partir do primeiro commit alterado em diante; isso significa problemas para qualquer um que baseou suas mudanças em seu branch pré-reescrever.
RECUPERAÇÃO
Se outros programadores não baseassem o seu trabalho na versão pré-reescrita, a solução mais simples seria re-clonar (clonar novamente).
Alternativamente, eles podem tentar {[[9]}, o que iria avançar rapidamente se não houvesse quaisquer alterações no seu repositório, ou ajustar o seu ramo em cima de commits reescritos (queremos evitar a junção, uma vez que iria manter os commits pré-reescritos para sempre). Tudo isto partindo do princÃpio de que eles não cometeram work; use git stash
para esconder as alterações de outra forma.
Se outros programadores usarem ramificações, e / ou git pull --rebase
não funcionarem, por exemplo, porque o upstream não está configurado, eles têm de ajustar a base o seu trabalho em cima dos commits pós-reescrita. Por exemplo, logo após a obtenção de novas alterações (git fetch
), para um ramo master
baseado em / bifurcado de origin/master
, é preciso executar
$ git rebase --onto origin/master origin/master@{1} master
Aqui origin/master@{1}
é o estado pré-reescrito (antes de obter), veja em gitrevisions .
A solução alternativa seria usar o mecanismorefs/replace/, disponÃvel no Git desde a versão 1.6.5. Nesta solução você fornece substituições para commits que têm email errado; então qualquer um que obtém refs 'replace' (algo como fetch = +refs/replace/*:refs/replace/*
refspec no lugar apropriado em seu .git/config
) obterá substituições de forma transparente, e aqueles que não vão buscar esses refs ver velhos commits.
-
Encontre todos os commits com e-mail errado, por exemplo usando
$ git log [email protected] --all
-
Para cada commit errado, crie uma commit de substituição e adicione-a à base de dados de objectos
$ 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>
-
Agora que corrigiu o commit na base de dados de objectos, tem de dizer ao git para substituir automaticamente e de forma transparente o commit errado, corrigindo um com a utilização
git replace
comando:$ git replace <ID of wrong commit> <ID of corrected commit>
-
Finalmente, listar todas as substituições para verificar se este procedimento sucede
$ git replace -l
E verificar se são efectuadas substituições
$ git log [email protected] --all
git replace
que não tem (ainda) o modo de lote, então você teria que usar o loop shell para isso, ou substituir "por mão".
NÃO FOI TESTADO! MMMV.
Note que você pode encontrar alguns cantos ásperos ao usar o mecanismo refs/replace/
: é novo, e ainda não muito bem testado .
Se os commits que deseja corrigir forem os mais recentes, e apenas alguns deles, poderá usar uma combinação de git reset
e git stash
para voltar a enviá-los de novo após configurar o nome e o e-mail correctos.
A sequência será algo como isto (para 2 commits errados, 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
Se você está usando Eclipse com EGit, então há uma solução bastante fácil.
Suposição: você tem commits em um ramo local 'local_master_user_x' que não pode ser empurrado para um ramo remoto 'master' por causa do usuário inválido.
- Verifique o ramo remoto 'mestre'
- Seleccione os projectos/pastas/ficheiros para os quais 'local_master_ user_x' contém alterações
- Botão direito - substituir por-Branch- 'local_master_ user_x'
- Commit these changes again, this tempo como Utilizador correcto e para a ramificação local 'master'
- empurrar para o 'mestre' remoto
Usando o rebase interactivo, poderá colocar um comando Emend após cada commit que quiser alterar. Por exemplo:
pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
Note que o git armazena dois endereços de email diferentes, um para o committer (a pessoa que cometeu a alteração) e outro para a author (a pessoa que escreveu a alteração).
A informação do 'committer' não é mostrada na maioria dos locais, mas você pode vê-la com git log -1 --format=%cn,%ce
(ou usar show
em vez de log
para indicar um 'commit' em particular).
Ao mudar o autor do seu último commit é tão simples como git commit --amend --author "Author Name <[email protected]>"
, não há um liner ou argumento para fazer o mesmo com a informação do committer.
A solução é (temporariamente, ou não) alterar a informação do utilizador, em seguida, alterar o commit, que irá actualizar o committer para a sua informação actual:
git config user.email [email protected]
git commit --amend
Passo 1: Mude o seu nome de utilizador no git para todos os commits Futuros, de acordo com as instruções aqui: https://help.github.com/articles/setting-your-username-in-git/
Passo 2: Executar o seguinte programa 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: verifique o seu repositório para um ficheiro temporário, verifique todos os ramos remotos, execute o script que irá reescrever o histórico, faça um empurrão de força do novo estado, e diga a todos os seus colegas para fazer um ajuste de base para obter as alterações.
Tivemos problemas em correr isto no OS X porque de alguma forma estragou as linhas das mensagens de commit, por isso tivemos de voltar a executá-lo numa máquina Linux depois.Depois de colocar esse script no seu caminho, você pode emitir comandos como:
-
Mudar os encontros do autor no ramo actual
$ git changemail -a [email protected] -n newname -m [email protected]
-
Mudar as combinações de autor e committer em
e . Passar -f
ao ramo do filtro para permitir reescrever as cópias de segurança$ git changemail -b [email protected] -n newname -m [email protected] -- -f <branch> <branch2>
-
Mostra os utilizadores existentes no repo
$ git changemail --show-both
Se você é o único usuário deste repo ou você não se importa com a possibilidade de quebrar o repo para outros usuários, então sim. Se você pressionou estes commits e eles existem onde outro lugar pode acessá-los, então não, a menos que você não se importa em quebrar repos de outras pessoas. O problema é que ao alterar estes commits você estará gerando novos SHAs que fará com que eles sejam tratados como commits diferentes. Quando alguém tenta puxar estes 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 tão YMMV)
Isto funciona em 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;
}
git rebase -i YOUR_FIRTS_COMMIT_SHA^
while true; do git commit --amend --author="Name Surname <[email protected]>" --no-edit && git rebase --continue; done
Carregue em ^C # após o ajuste de Base estar feito (o ciclo irá continuar a actualizar a última persistência)
bash <(curl -s https://raw.githubusercontent.com/majdarbash/git-author-change-script/master/run.sh)
Referência: https://github.com/majdarbash/git-author-change-script
#!/bin/sh
PWD=`pwd`
if [[ $PWD == *"Ippon"* ]] # 1)
then
EMAIL=$(git config user.email)
if [[ $EMAIL == *"Work"* ]] # 2)
then
echo "";
else
echo "Email not configured to your Work email in the Work directory.";
git config user.email "[email protected]"
echo "Git email configuration has now been changed to \"$(git config user$
echo "\nPlease run your command again..."
echo ''
exit 1
fi;
elif [[ $PWD == *"Personal"* ]]
then
EMAIL=$(git config user.email)
if [[ $EMAIL == "[email protected]" ]]
then
echo "";
else
echo "Email is not configured to your personal account in the Personal di$
git config user.email "[email protected]"
echo "Git email configuration has now been changed to \"$(git config user$
echo "\nPlease run your command again..."
echo ''
exit 1;
fi;
fi;
Verifica a sua pasta de trabalho actual, depois verifica se o seu git está configurado para o e-mail correcto. Caso contrário, muda automaticamente. Veja os detalhes completos aqui.