Bagaimana cara mengubah nama pengarang dan penglaju dan mengirim banyak komit di Git?

2392

Saya sedang menulis skrip sederhana di komputer sekolah, dan melakukan perubahan pada Git (dalam repo yang ada di flashdisk saya, dikloning dari komputer saya di rumah). Setelah melakukan beberapa kali, saya menyadari bahwa saya melakukan hal-hal sebagai pengguna root.

Apakah ada cara untuk mengubah pembuat komitmen ini dengan nama saya?

Flávio Amieiro
sumber
13
Pertanyaan: apakah menggunakan cabang git filter mempertahankan SHA1 untuk tag, versi, dan objek sebelumnya? Atau akan mengubah kekuatan nama penulis mengubah SHA1 terkait juga?
AndyL
36
Hash akan berubah ya
Tidak Tersedia
3
Intinya, saya membuat skrip kecil yang akhirnya memperbaiki akar masalah bagi saya. gist.github.com/tripleee/16767aa4137706fd896c
tripleee
2
@impinball Usia pertanyaan hampir tidak relevan. Membuat pertanyaan duplikat baru adalah hal yang mustahil. Saya kira saya bisa membuat pertanyaan yang meminta jawaban khusus ini, tetapi saya tidak sepenuhnya yakin itu akan mendapatkan visibilitas yang begitu banyak. Bukannya ada kekurangan pertanyaan Git di sini ... Senangnya saya bisa membantu.
tripleee
8
GitHub memiliki skrip khusus untuk ini: help.github.com/articles/changing-author-info
Timur Bernikovich

Jawaban:

1214

Jawaban ini menggunakan git-filter-branch, yang sekarang diberikan peringatan oleh para dokter :

cabang-git filter memiliki sejumlah besar jebakan yang dapat menghasilkan pepohonan yang tidak jelas dari penulisan ulang sejarah yang dimaksudkan (dan dapat memberi Anda sedikit waktu untuk menyelidiki masalah seperti itu karena memiliki kinerja yang sangat buruk). Masalah keselamatan dan kinerja ini tidak dapat diperbaiki secara terbalik dan karenanya, penggunaannya tidak disarankan. Silakan gunakan alat pemfilteran riwayat alternatif seperti git filter-repo . Jika Anda masih perlu menggunakan cabang git filter, harap baca dengan hati-hati KEAMANAN (dan KINERJA ) untuk mempelajari tentang ranjau darat cabang filter, dan kemudian dengan hati-hati menghindari sebanyak mungkin bahaya yang terdaftar di sana sejauh mungkin.

Mengubah penulis (atau penglaju) akan membutuhkan penulisan ulang semua sejarah. Jika Anda setuju dengan itu dan berpikir itu layak maka Anda harus memeriksa cabang-filter git . Halaman manual berisi beberapa contoh untuk membantu Anda memulai. Perhatikan juga bahwa Anda dapat menggunakan variabel lingkungan untuk mengubah nama pembuat, pengalih, tanggal, dll. - lihat bagian "Variabel Lingkungan" dari halaman git man .

Secara khusus, Anda dapat memperbaiki semua nama penulis dan email yang salah untuk semua cabang dan tag dengan perintah ini (sumber: bantuan 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
sumber
612
Github memiliki skrip publik untuk help.github.com/articles/changing-author-info dan ini berfungsi dengan baik!
defvol
34
Setelah menjalankan skrip, Anda dapat menghapus cabang cadangan dengan menjalankan "git update-ref -d ref / original / refs / head / master".
DR
7
@rodowi, itu menggandakan semua komitmen saya.
Rafael Barros
6
@RafaelBarros info penulis (sama seperti hal lain dalam sejarah) adalah bagian dari kunci sha komit. Setiap perubahan pada riwayat adalah penulisan ulang yang mengarah ke id baru untuk semua komit. Jadi jangan menulis ulang pada repo bersama atau pastikan semua pengguna menyadarinya ...
johannes
20
Dipecahkan menggunakangit push --force --tags origin HEAD:master
mcont
1577

CATATAN: Jawaban ini mengubah SHA1s, jadi berhati-hatilah menggunakannya pada cabang yang telah didorong. Jika Anda hanya ingin memperbaiki ejaan nama atau memperbarui email lama, git memungkinkan Anda melakukan ini tanpa menulis ulang riwayat menggunakan .mailmap. Lihat jawaban saya yang lain .

Menggunakan Interaktif Rebase

Anda bisa melakukannya

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

Kemudian tandai semua komit buruk Anda sebagai "edit" di file rebase. Jika Anda juga ingin mengubah komit pertama Anda, Anda harus menambahkannya secara manual sebagai baris pertama dalam file rebase (ikuti format dari baris lain). Kemudian, ketika git meminta Anda untuk mengubah setiap commit, lakukan

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

edit atau tutup saja editor yang terbuka, lalu lakukan

git rebase --continue

untuk melanjutkan rebase.

Anda bisa melewatkan membuka editor sama sekali di sini dengan menambahkan --no-edit sehingga perintahnya adalah:

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

Komit tunggal

Seperti yang dicatat oleh beberapa komentator, jika Anda hanya ingin mengubah komit terbaru, perintah rebase tidak diperlukan. Kerjakan saja

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

Ini akan mengubah penulis menjadi nama yang ditentukan, tetapi committer akan diatur ke pengguna yang dikonfigurasi di git config user.namedan git config user.email. Jika Anda ingin mengatur committer ke sesuatu yang Anda tentukan, ini akan mengatur penulis dan committer:

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

Catatan tentang Menggabungkan Komit

Ada sedikit cacat dalam respons awal saya. Jika ada komitmen gabungan antara saat ini HEADdan Anda <some HEAD before all your bad commits>, maka git rebaseakan meratakannya (dan omong-omong, jika Anda menggunakan permintaan tarik GitHub, akan ada banyak komitmen gabungan dalam riwayat Anda). Hal ini seringkali dapat menyebabkan sejarah yang sangat berbeda (karena perubahan duplikat mungkin "diubah"), dan dalam kasus terburuk, hal itu dapat menyebabkan git rebaseAnda meminta untuk menyelesaikan konflik penggabungan yang sulit (yang kemungkinan sudah diselesaikan dalam komitmen gabungan). Solusinya adalah dengan menggunakan -pbendera git rebase, yang akan mempertahankan struktur penggabungan sejarah Anda. Halaman untuk git rebasememperingatkan bahwa menggunakan -pdan -idapat menyebabkan masalah, tetapi dalamBUGS bagian yang bertuliskan "Mengedit melakukan dan menulis ulang pesan komit mereka akan berfungsi dengan baik."

Saya telah menambahkan -pperintah di atas. Untuk kasus di mana Anda baru saja mengubah komit terbaru, ini bukan masalah.

penilai
sumber
27
Bagus untuk komit ganjil - berguna jika Anda berpasangan dan lupa untuk mengubah penulis
mloughran
32
+1 untuk menyebutkan usecase untuk perbaikan satu kesalahan yang khas: git commit --amend --author = nama pengguna
Nathan Kidd
12
Ini sempurna, penggunaan kata kunci saya yang paling umum adalah saya duduk di komputer lain dan lupa membuat penulis dan dengan demikian biasanya memiliki <5 komitmen atau lebih untuk memperbaikinya.
Zitrax
57
git commit --amend --reset-authorjuga berfungsi sekali user.namedan user.emaildikonfigurasi dengan benar.
Poin
14
Tulis ulang info penulis di semua komit setelah <commit>menggunakan user.namedan user.emaildari ~/.gitconfig: jalankan git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', simpan, keluar. Tidak perlu diedit!
ntc2
588

Anda juga dapat melakukan:

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

Catatan, jika Anda menggunakan perintah ini di prompt perintah Windows, maka Anda harus menggunakan "alih-alih ':

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
sumber
4
Tidak menggunakan env-filter solusi yang lebih mudah? Tidak yakin mengapa ini mendapatkan lebih banyak suara, kalau begitu.
stigkj
3
Kemudian tautan terputus. Bagaimana kita mendorong perubahan ini ke repositori lain?
Russell
28
env-filter akan mengubah semua komitmen. Solusi ini memungkinkan bersyarat.
user208769
5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"maaf tapi di mana -f-flag akan berada ketika menjalankan skrip ini dua kali. Sebenarnya itu ada dalam jawaban Brian, maaf tentang gangguan tepat setelah cabang-filter adalah solusinya.
hhh
2
@ user208769 env-filter juga memungkinkan bersyarat; lihat jawaban saya :-)
stigkj
559

Satu liner, tetapi berhati-hatilah jika Anda memiliki repositori multi-pengguna - ini akan mengubah semua komit untuk memiliki penulis dan pengangkat yang sama (baru).

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

Dengan linebreak di string (yang dimungkinkan dalam 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
sumber
Poin kecil, ekspor sebenarnya berlebihan meskipun tidak ada salahnya. misalnya git-filter-branch --env-filter "GIT_AUTHOR_NAME = 'Nama baru'; GIT_AUTHOR_EMAIL = 'Email baru'" KEPALA.
Alec the Geek
4
Mengapa itu menulis ulang semua komit jika Anda menentukan HEADdi akhir perintah?
Nick Volynkin
1
Ini tidak berfungsi untuk repositori bitbucket saya, ada ide? Saya melakukan git push --force --tags origin 'refs/heads/*'setelah perintah yang disarankan
Olorin
1
Perintah push untuk ini adalah:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK
1
Rapi; ini juga membuat stempel waktu lama.
DharmaTurtle
221

Ini terjadi ketika Anda tidak menginisialisasi $ HOME / .gitconfig. Anda dapat memperbaikinya sebagai:

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

diuji dengan git versi 1.7.5.4

lrkwz
sumber
9
Itu bekerja sangat baik pada komit terakhir. Bagus dan sederhana. Tidak harus menjadi perubahan global, menggunakan --localkarya juga
Ben
Yang ini adalah pemenang besar bagi saya! The git commit --amend --reset-author --no-editPerintah ini sangat berguna jika Anda membuat komit dengan informasi penulis yang salah, kemudian mengatur penulis yang benar setelah-the-fakta melalui git config. Menyimpan $$ saya sekarang ketika saya harus memperbarui email saya.
ecbrodie
187

Untuk satu komit:

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

(diekstraksi dari jawaban asmeurer's)

kebiru-biruan
sumber
14
tapi itu hanya jika itu adalah komitmen terbaru
Richard
4
Menurut git help commit, git commit --amendperubahan komit di "ujung cabang saat ini" (yaitu KEPALA). Ini biasanya komit terbaru, tetapi Anda bisa membuatnya komit yang Anda inginkan dengan terlebih dahulu memeriksa komit itu dengan git checkout <branch-name>atau git checkout <commit-SHA>.
Rory O'Kane
12
Tetapi jika Anda melakukannya, semua komit yang sudah memiliki komit sebagai orangtua akan menunjuk komit yang salah. Lebih baik menggunakan filter-branch pada saat itu.
John Gietzen
3
@JohnGietzen: Anda dapat mengubah komit kembali ke yang diubah untuk memperbaikinya. Namun, jika Anda melakukan> 1 komit, maka seperti yang disebutkan, cabang-filter mungkin akan jauh lebih mudah.
Thanatos
5
Perhatikan bahwa perubahan ini hanya komit authordan bukancommitter
Nick Volynkin
179

Dalam kasus di mana hanya beberapa komit teratas memiliki penulis yang buruk, Anda dapat melakukan ini semua di dalam git rebase -imenggunakan execperintah dan --amendkomit, sebagai berikut:

git rebase -i HEAD~6 # as required

yang memberi Anda daftar komitmen yang dapat diedit:

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

Kemudian tambahkan exec ... --author="..."baris setelah semua baris dengan penulis yang buruk:

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

simpan dan keluar dari editor (untuk menjalankan).

Solusi ini mungkin lebih panjang untuk diketik daripada yang lain, tetapi sangat dapat dikontrol - Saya tahu persis apa yang dilakukan.

Terima kasih kepada @asmeurer untuk ilhamnya.

Alex Brown
sumber
26
Sangat mengagumkan. Bisakah Anda mempersingkat dengan mengatur user.name dan user.email di konfigurasi lokal repo, dan kemudian setiap baris hanya exec git commit --amend --reset-author -C HEAD?
Andrew
1
Jawaban kanonik, untuk menggunakan cabang-filter, cukup hapus ref / kepala / master untuk saya. Jadi +1 ke solusi Anda yang dapat dikontrol dan diedit. Terima kasih!
jmtd
Mengapa Anda memulainya dengan Someone else's commitbukan my bad commit 1? Saya hanya mencoba HEAD^^mengubah 2 komitmen terakhir, dan itu berfungsi dengan baik.
fredoverflow
3
Di tempat git rebase -i HEAD^^^^^^Anda juga dapat menulisgit rebase -i HEAD~6
Patrick Schlüter
1
Harap perhatikan bahwa ini mengubah stempel waktu dari komit. Lihat stackoverflow.com/a/11179245/1353267 untuk kembali ke stempel waktu yang benar
Samveen
111

Github memiliki solusi yang bagus , yaitu skrip shell berikut:

#!/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
sumber
5
Bekerja dengan sempurna. Hanya perlu git reset --hard HEAD^beberapa kali di repositori lokal lainnya untuk mendapatkan mereka ke versi sebelumnya, git pull-dengan versi yang diubah, dan di sini saya tanpa baris yang mengandung unknown <[email protected]>(harus suka default git).
Alan Plum
1
Saya tidak bisa mendorong setelah ini. Apakah saya harus menggunakan "-f"?
Fish Monitor
9
Saya lakukan git push -f. Juga, repo lokal harus dikloning kembali setelah ini.
Fish Monitor
Jika Anda perlu menjalankan skrip shell pada cabang tertentu, Anda dapat mengubah baris terakhir menjadi: "master..nama-cabang-nama Anda" (dengan asumsi Anda bercabang master).
Robert Kajic
Klik tautan <solusi bagus> karena skrip telah diperbarui
gxpr
82

Seperti yang disebutkan oleh docgnome, menulis ulang sejarah itu berbahaya dan akan merusak gudang orang lain.

Tetapi jika Anda benar-benar ingin melakukan itu dan Anda berada dalam lingkungan bash (tidak ada masalah di Linux, di Windows, Anda dapat menggunakan git bash, yang disediakan dengan instalasi git), gunakan cabang-git filter :

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

Untuk mempercepat, Anda dapat menentukan rentang revisi yang ingin Anda tulis ulang:

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
sumber
2
Perhatikan bahwa ini akan meninggalkan tag yang menunjuk ke komit yang lama. --tag-name-filter catadalah opsi "membuatnya bekerja".
Roman Starkov
@romkyn punya gagasan tentang cara mengubah tag juga?
Nick Volynkin
@NickVolynkin Ya, Anda tentukan --tag-name-filter cat. Ini seharusnya merupakan perilaku default.
Roman Starkov
48

Saat mengambil alih komitmen yang tidak dihapus dari penulis lain, ada cara mudah untuk menangani ini.

git commit --amend --reset-author

Ryanmt
sumber
1
Untuk satu komit, dan jika Anda ingin memasukkan nama pengguna Anda, ini adalah cara yang paling mudah.
Pedro Benevides
7
Anda dapat menambahkan --no-edituntuk membuat ini lebih mudah, karena umumnya kebanyakan orang hanya ingin memperbarui alamat email dan bukan pesan komit
PlagueHammer
Bisakah kalian berbagi perintah git hanya untuk memperbarui email / nama pengguna komit terakhir dengan yang baru
adi
Apakah kamu mencoba ini? Itu seharusnya menjadi efek samping dari ini, jika tidak stackoverflow.com/a/2717477/654245 sepertinya jalan yang baik.
Ryanmt
47

Anda dapat menggunakan ini sebagai alias sehingga Anda dapat melakukan:

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

atau selama 10 komit terakhir:

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

Tambahkan ke ~ / .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 "

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

Semoga bermanfaat.

brauliobo
sumber
"git: 'change-commit' bukan perintah git. Lihat 'git --help'."
Native_Mobile_Arch_Dev
Setelah perintah ini & sinkronkan dengan master semua komit dalam riwayat digandakan! Bahkan dari pengguna lain :(
Vladimir
@Vladimir yang diharapkan, silakan belajar tentang mengubah sejarah di git
brauliobo
Bagi saya sepertinya berjalan di / bin / sh, jadi saya harus mengganti tes khusus-bash [[ ]]dengan tes yang kompatibel dengan sh [ ](kurung tunggal). Selain itu berfungsi dengan baik, terima kasih!
Steffen Schwigon
39

Ini adalah versi @ Brian versi yang lebih rumit:

Untuk mengubah pembuat dan pengalih, Anda dapat melakukan ini (dengan linebreak di string yang dimungkinkan dalam 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

Anda mungkin mendapatkan salah satu dari kesalahan ini:

  1. Direktori sementara sudah ada
  2. Referensi yang dimulai dengan referensi / asli sudah ada
    (ini berarti cabang-filter lain telah dijalankan sebelumnya di repositori dan referensi cabang asli didukung di referensi / asli )

Jika Anda ingin memaksa menjalankan terlepas dari kesalahan ini, tambahkan --forcebendera:

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

Sedikit penjelasan tentang -- --allopsi mungkin diperlukan: Ini membuat cabang-filter bekerja pada semua revisi pada semua referensi (yang mencakup semua cabang). Ini berarti, misalnya, bahwa tag juga ditulis ulang dan terlihat pada cabang yang ditulis ulang.

"Kesalahan" yang umum adalah HEADsebagai gantinya, yang berarti memfilter semua revisi hanya pada cabang saat ini . Dan kemudian tidak ada tag (atau referensi lainnya) akan ada di cabang yang ditulis ulang.

stigkj
sumber
Kudos untuk memasok prosedur yang mengubah komit pada semua referensi / cabang.
Johnny Utahh
25

Satu perintah untuk mengubah penulis untuk komit N terakhir:

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

CATATAN

  • yang --no-editmerek bendera yakin git commit --amendtidak meminta konfirmasi tambahan
  • Ketika Anda menggunakan git rebase -i, Anda dapat secara manual memilih komit tempat untuk mengubah penulis,

file yang Anda edit akan terlihat seperti ini:

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

Anda masih dapat memodifikasi beberapa baris untuk melihat di mana Anda ingin mengubah penulis. Ini memberi Anda jalan tengah yang bagus antara otomatisasi dan kontrol: Anda melihat langkah-langkah yang akan berjalan, dan begitu Anda menyimpan semuanya akan diterapkan sekaligus.

Chris Maes
sumber
Luar biasa! Terima kasih!
Pablo Lalloni
Saya menggunakan HEAD ~ 8 dan itu menunjukkan jauh lebih dari 8 komit terakhir.
Bryan Bryce
1
@BryanBryce jika ada komitmen gabungan yang terlibat, semuanya menjadi rumit :)
Chris Maes
@ ChrisMaes Ah, saya mengerti apa yang terjadi. Saya tidak ingin mengacaukannya, hanya di cabang saya.
Bryan Bryce
Dalam hal ini, jika Anda bercabang dari master, Anda dapat:git rebase -i master -x ...
Chris Maes
23
  1. Lari git rebase -i <sha1 or ref of starting point>
  2. tandai semua komit yang ingin Anda ubah dengan edit(atau e)
  3. lilitkan dua perintah berikut hingga Anda telah memproses semua komitmen:

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

Ini akan menyimpan semua informasi komit lainnya (termasuk tanggal). The --reuse-message=HEADpilihan mencegah editor pesan dari launching.

sporsh
sumber
23

Saya menggunakan yang berikut ini untuk menulis ulang penulis untuk seluruh repositori, termasuk tag dan semua cabang:

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

Kemudian, seperti yang dijelaskan di halaman MAN cabang-filter , hapus semua referensi asli yang didukung oleh filter-branch(ini destruktif, cadangan dulu):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Ton van den Heuvel
sumber
2
Ini sangat penting untuk digunakan --tag-name-filter cat. Kalau tidak, tag Anda akan tetap berada di rantai komit asli. Jawaban lain gagal menyebutkan ini.
jeberle
21

Saya mengadaptasi solusi ini yang berfungsi dengan menelan sederhana author-conv-file(formatnya sama dengan git-cvsimport ). Ini bekerja dengan mengubah semua pengguna sebagaimana didefinisikan dalamauthor-conv-file semua cabang.

Kami menggunakan ini dalam hubungannya dengan cvs2git untuk memigrasi repositori kami dari cvs ke git.

yaitu Sampel author-conv-file

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

Naskah:

 #!/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
sumber
Terima kasih, saya bertanya-tanya mengapa ini bukan fungsionalitas inti git (atau git-svn). Ini dapat dilakukan dengan flag untuk git svn clone, tetapi tidak di git filter-branch ...
Daniel Hershcovich
20

Saya harus menunjukkan bahwa jika satu-satunya masalah adalah bahwa penulis / email berbeda dari biasanya, ini bukan masalah. Perbaikan yang benar adalah dengan membuat file yang disebut .mailmapdi dasar direktori dengan garis-garis seperti

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

Dan sejak saat itu, perintah seperti git shortlogakan menganggap kedua nama itu sama (kecuali jika Anda secara khusus mengatakannya tidak demikian). Lihat http://schacon.github.com/git/git-shortlog.html untuk informasi lebih lanjut.

Ini memiliki keunggulan dari semua solusi lain di sini karena Anda tidak perlu menulis ulang sejarah, yang dapat menyebabkan masalah jika Anda memiliki upstream, dan selalu merupakan cara yang baik untuk kehilangan data secara tidak sengaja.

Tentu saja, jika Anda melakukan sesuatu seperti diri Anda sendiri dan itu harus benar-benar orang lain, dan Anda tidak keberatan menulis ulang sejarah pada saat ini, mengubah penulis komit mungkin merupakan ide yang baik untuk tujuan atribusi (dalam hal ini saya mengarahkan Anda ke saya jawaban lain di sini).

penilai
sumber
18

Saya menemukan versi yang disajikan cara agresif, terutama jika Anda melakukan patch dari pengembang lain, ini pada dasarnya akan mencuri kode mereka.

Versi di bawah ini berfungsi pada semua cabang dan mengubah penulis dan komitter secara terpisah untuk mencegahnya.

Kudos to leif81 untuk semua opsi.

#!/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
sumber
18
  1. Ubah komit author name & emaildengan Amend, lalu ganti 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. Cara lain 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
sumber
2
Jawaban yang sangat bagus Saya suka bahwa perubahan dibungkus dari pembaruan untuk membersihkan komit git
Aleks
12

Cara tercepat dan termudah untuk melakukan ini adalah dengan menggunakan argumen --exec dari git rebase:

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

Ini akan membuat daftar todo yang terlihat seperti ini:

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

dan ini akan bekerja secara otomatis, yang berfungsi saat Anda memiliki ratusan komitmen.

Lie Ryan
sumber
9

Jika Anda adalah satu-satunya pengguna repositori ini, Anda dapat menulis ulang riwayat menggunakan salah satu git filter-branch(seperti svick menulis ), atau git fast-export/ git fast-importplus skrip filter (seperti yang dijelaskan dalam artikel yang dirujuk dalam jawaban docgnome ), atau rebase interaktif . Tetapi salah satu dari mereka akan mengubah revisi dari komit pertama yang diubah dan seterusnya; ini berarti masalah bagi siapa saja yang mendasarkan perubahannya pada pra-penulisan cabang Anda.

PEMULIHAN

Jika pengembang lain tidak mendasarkan pekerjaan mereka pada versi pra-menulis ulang, solusi paling sederhana adalah mengkloning ulang (clone lagi).

Atau mereka dapat mencoba git rebase --pull, yang akan mempercepat jika tidak ada perubahan dalam repositori mereka, atau rebase cabang mereka di atas komit yang ditulis ulang (kami ingin menghindari penggabungan, karena itu akan membuat pra-penulisan ulang komit selamanya). Semua ini dengan asumsi bahwa mereka tidak memiliki pekerjaan; gunakan git stashuntuk menyembunyikan perubahan jika tidak.

Jika pengembang lain menggunakan cabang fitur, dan / atau git pull --rebasetidak bekerja misalnya karena hulu tidak diatur, mereka harus rebase pekerjaan mereka di atas komit pasca-rewrite. Sebagai contoh tepat setelah mengambil perubahan baru ( git fetch), untuk mastercabang berdasarkan / bercabang dari origin/master, orang perlu menjalankan

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

Ini origin/master@{1}adalah kondisi pra-penulisan ulang (sebelum mengambil), lihat gitrevision .


Solusi alternatif adalah menggunakan referensi / ganti / mekanisme, tersedia di Git sejak versi 1.6.5. Dalam solusi ini Anda memberikan penggantian untuk komit yang memiliki email yang salah; lalu siapa pun yang mengambil ref 'ganti' (sesuatu seperti fetch = +refs/replace/*:refs/replace/*refspec di tempat yang tepat di mereka .git/config ) akan mendapatkan pengganti transparan, dan mereka yang tidak mengambil orang-orang ref akan melihat komit tua.

Prosedurnya kira-kira seperti ini:

  1. Temukan semua komit dengan email yang salah, misalnya menggunakan

    $ git log [email protected] --all
    
  2. Untuk setiap komit yang salah, buat komit pengganti, dan tambahkan ke objek database

    $ 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. Sekarang setelah Anda mengkoreksi komit dalam database objek, Anda harus memberi tahu git untuk secara otomatis dan transparan mengganti komit yang salah dengan mengkoreksi komit menggunakan git replaceperintah:

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Akhirnya, daftarkan semua penggantian untuk memeriksa apakah prosedur ini berhasil

    $ git replace -l
    

    dan periksa apakah penggantian dilakukan

    $ git log [email protected] --all
    

Anda tentu saja dapat mengotomatiskan prosedur ini ... well, semua kecuali menggunakan git replaceyang tidak memiliki mode batch (belum), jadi Anda harus menggunakan shell loop untuk itu, atau mengganti "dengan tangan".

TIDAK DIUJI! YMMV.

Perhatikan bahwa Anda mungkin menemukan beberapa sudut kasar saat menggunakan refs/replace/mekanisme: itu baru, dan belum diuji dengan sangat baik .

Jakub Narębski
sumber
6

Jika komit yang ingin Anda perbaiki adalah yang terbaru, dan hanya beberapa, Anda dapat menggunakan kombinasi dari git resetdangit stash untuk mengembalikan komit mereka lagi setelah mengkonfigurasi nama dan email yang tepat.

Urutannya akan seperti ini (untuk 2 komit yang salah, tidak ada perubahan yang tertunda):

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

Jika Anda menggunakan Eclipse dengan EGit, maka ada solusi yang cukup mudah.
Asumsi: Anda memiliki komit di cabang lokal 'local_master_user_x' yang tidak dapat didorong ke 'master' cabang jauh karena pengguna yang tidak valid.

  1. Periksa 'master' cabang jarak jauh
  2. Pilih proyek / folder / file yang berisi 'local_master_user_x' berisi perubahan
  3. Klik kanan - Ganti dengan - Cabang - 'local_master_user_x'
  4. Lakukan perubahan ini lagi, kali ini sebagai pengguna yang benar dan masuk ke 'master' cabang lokal
  5. Dorong ke 'master' jarak jauh
paphko
sumber
5

Menggunakan rebase interaktif, Anda dapat menempatkan perintah amend setelah setiap komit yang ingin Anda ubah. Contohnya:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
j16r
sumber
3
Masalahnya adalah metadata komit lainnya (mis. Tanggal dan waktu) juga diubah. Saya baru saja menemukan itu dengan cara yang sulit ;-).
halfer
5

Perhatikan bahwa git menyimpan dua alamat email yang berbeda, satu untuk committer (orang yang melakukan perubahan) dan satu lagi untuk penulis (orang yang menulis perubahan).

Informasi committer tidak ditampilkan di sebagian besar tempat, tetapi Anda dapat melihatnya dengan git log -1 --format=%cn,%ce(atau menggunakan showdaripada logmenentukan komit tertentu).

Sementara mengubah pembuat komit terakhir Anda sesederhana git commit --amend --author "Author Name <[email protected]>" , tidak ada satu kalimat atau argumen untuk melakukan hal yang sama terhadap informasi committer.

Solusinya adalah (untuk sementara, atau tidak) mengubah informasi pengguna Anda, kemudian mengubah komit, yang akan memperbarui pengalih ke informasi Anda saat ini:

git config user.email [email protected] 
git commit --amend
Pak Athos
sumber
Perhatikan bahwa nilai lama masih di beberapa tempat di path\to\repo\.git. Saya belum yakin apa yang perlu Anda lakukan untuk menghapusnya sepenuhnya. Amend sayangnya (?) Tampaknya tidak menghapus.
ruffin
5

Kami telah mengalami masalah hari ini di mana karakter UTF8 dalam nama penulis menyebabkan masalah pada server build, jadi kami harus menulis ulang riwayat untuk memperbaikinya. Langkah-langkah yang diambil adalah:

Langkah 1: Ubah nama pengguna Anda di git untuk semua komitmen di masa mendatang, sesuai instruksi di sini: https://help.github.com/articles/setting-your-username-in-git/

Langkah 2: Jalankan skrip bash berikut:

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

Gambaran singkat: Periksa repositori Anda ke file temp, checkout semua cabang jarak jauh, jalankan skrip yang akan menulis ulang sejarah, lakukan dorongan paksa negara baru, dan beri tahu semua kolega Anda untuk melakukan rebase pull untuk mendapatkan perubahan.

Kami mengalami masalah dengan menjalankan ini pada OS X karena entah bagaimana mengacaukan akhir baris dalam pesan komit, jadi kami harus menjalankannya kembali pada mesin Linux setelahnya.

Miloš Ranđelović
sumber
5

Masalah Anda sangat umum. Lihat " Menggunakan Mailmap untuk Memperbaiki Daftar Penulis di Git "

Demi kesederhanaan, saya telah membuat skrip untuk memudahkan proses: git-changemail

Setelah meletakkan skrip itu di jalur Anda, Anda dapat mengeluarkan perintah seperti:

  • Ubah kecocokan penulis pada cabang saat ini

    $ git changemail -a [email protected] -n newname -m [email protected]
    
  • Ubah pencocokan penulis dan penglaju di <branch> dan <branch2>. Lulus -fke filter-branch untuk memungkinkan penulisan ulang cadangan

    $ git changemail -b [email protected] -n newname -m [email protected] -- -f &lt;branch> &lt;branch2>
    
  • Tampilkan pengguna yang ada di repo

    $ git changemail --show-both
    

Ngomong-ngomong, setelah melakukan perubahan, bersihkan cadangan dari cabang-filter dengan: git-backup-clean

Albfan
sumber
1
ketika saya menjalankan perintah Anda, dikatakan "fatal: cant exec 'git-changemail': Izin ditolak"
Govind
3

Saya ingin menambahkan Contoh saya juga. Saya ingin membuat bash_function dengan parameter yang diberikan.

ini bekerja di 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
sumber
2

Jika Anda adalah satu-satunya pengguna repo ini atau Anda tidak peduli kemungkinan melanggar repo untuk pengguna lain, maka ya. Jika Anda telah mendorong komitmen ini dan ada di mana tempat lain dapat mengaksesnya, maka tidak, kecuali Anda tidak peduli tentang melanggar repo orang lain. Masalahnya adalah dengan mengubah komit ini, Anda akan menghasilkan SHA baru yang akan menyebabkan mereka diperlakukan sebagai komit yang berbeda. Ketika orang lain mencoba menarik komitmen yang diubah ini, sejarahnya berbeda dan kaboom.

Halaman ini http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html menjelaskan cara melakukannya. (Saya belum mencoba ini, YMMV)

baudtack
sumber
Jadi, tidak ada cara aman untuk menulis ulang user.email. Tanpa meledakkan orang lain. Saya tahu bahwa menulis ulang sejarah adalah ide yang buruk, saya hanya berpikir bahwa mungkin ada cara yang bersih untuk melakukannya dengan aman. Terima kasih.
manumoomoo
@mediaslave: Coba refs/replace/mekanisme.
Jakub Narębski
meta.stackexchange.com/a/8259/184684 - alias, jumlah tautan untuk membuatnya menjadi jawaban.
ruffin