Câu hỏi Làm thế nào để thay đổi tên tác giả và người gửi và e-mail của nhiều cam kết trong Git?


Tôi đã viết một kịch bản đơn giản trong máy tính của trường, và cam kết những thay đổi đối với Git (trong một repo trong pendrive của tôi, nhân bản từ máy tính của tôi ở nhà). Sau một vài lần commit, tôi nhận ra mình đã cam kết với tư cách là người dùng root.

Có cách nào để thay đổi tác giả của những cam kết này thành tên của tôi không?


2004
2018-04-15 03:09


gốc


Câu hỏi: sử dụng git filter-branch có bảo toàn SHA1 cho các thẻ, phiên bản và các đối tượng trước không? Hoặc sẽ thay đổi tên tác giả tên thay đổi SHA1 liên quan là tốt? - AndyL
Phát ban sẽ thay đổi có - Not Available
Tiếp tục, tôi tạo ra một kịch bản nhỏ mà cuối cùng đã sửa chữa nguyên nhân gốc rễ cho tôi. gist.github.com/tripleee/16767aa4137706fd896c - tripleee
@impinball Độ tuổi của câu hỏi khó có liên quan. Tạo câu hỏi trùng lặp mới không nằm trong câu hỏi. Tôi cho rằng tôi có thể tạo ra một câu hỏi đặt ra câu trả lời đặc biệt này nhưng tôi không hoàn toàn tin rằng nó sẽ có được nhiều khả năng hiển thị. Nó không giống như có một thiếu các câu hỏi của Git ở đây ... Vui vì tôi có thể giúp đỡ, dù sao đi nữa. - tripleee
GitHub có kịch bản đặc biệt cho việc này: help.github.com/articles/changing-author-info - Timur Bernikowich


Các câu trả lời:


Thay đổi tác giả (hoặc người bắt đầu) sẽ yêu cầu viết lại toàn bộ lịch sử. Nếu bạn ổn với điều đó và nghĩ rằng nó có giá trị nó thì bạn nên kiểm tra git filter-branch. Trang người đàn ông bao gồm một số ví dụ để giúp bạn bắt đầu. Cũng lưu ý rằng bạn có thể sử dụng các biến môi trường để thay đổi tên của tác giả, ký tự, ngày tháng, v.v. - xem phần "Biến môi trường" của git man page.

Cụ thể, bạn có thể sửa tất cả tên và email của tác giả sai cho tất cả các chi nhánh và thẻ với lệnh này (nguồn: Trợ giúp GitHub):

#!/bin/sh

git filter-branch --env-filter '
OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

801
2018-04-15 03:16



Github có một kịch bản công cộng cho rằng help.github.com/articles/changing-author-info và nó hoạt động rất tốt! - rodowi
Sau khi thực thi kịch bản, bạn có thể loại bỏ nhánh sao lưu bằng cách thực thi "git update-ref -d refs / original / refs / heads / master". - D.R.
@rodowi, nó sao chép tất cả các cam kết của tôi. - Rafael Barros
@RafaelBarros thông tin tác giả (giống như bất kỳ thông tin nào khác trong lịch sử) là một phần của khóa sha của commit. Mọi thay đổi đối với lịch sử là viết lại dẫn đến id mới cho tất cả các cam kết. Vì vậy, đừng viết lại trên repo được chia sẻ hoặc đảm bảo rằng tất cả người dùng đều biết về nó ... - johannes
Giải quyết bằng cách sử dụng git push --force --tags origin HEAD:master - Matteo Contrini


Sử dụng tương tác Rebase

Bạn có thể làm

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

Sau đó đánh dấu tất cả các cam kết xấu của bạn là "chỉnh sửa" trong tệp rebase. Nếu bạn cũng muốn thay đổi cam kết đầu tiên của mình, bạn phải tự thêm nó làm dòng đầu tiên trong tệp rebase (theo định dạng của các dòng khác). Sau đó, khi git yêu cầu bạn sửa đổi từng cam kết, hãy làm

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

chỉnh sửa hoặc chỉ đóng trình chỉnh sửa mở ra và sau đó thực hiện

git rebase --continue

để tiếp tục việc rebase.

Bạn có thể bỏ qua việc mở trình chỉnh sửa hoàn toàn tại đây bằng cách thêm vào --no-edit để lệnh sẽ là:

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

Cam kết đơn

Như một số người bình luận đã lưu ý, nếu bạn chỉ muốn thay đổi cam kết mới nhất, lệnh rebase là không cần thiết. Cứ làm đi

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

Điều này sẽ thay đổi tác giả thành tên được chỉ định, nhưng người chuyển sẽ được đặt thành người dùng được định cấu hình của bạn trong git config user.name và git config user.email. Nếu bạn muốn đặt người chuyển vào nội dung bạn chỉ định, điều này sẽ đặt cả tác giả và người nhận:

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

Lưu ý về Hợp nhất Cam kết

Có một lỗi nhỏ trong phản ứng ban đầu của tôi. Nếu có bất kỳ hợp nhất nào cam kết giữa hiện tại HEAD và của bạn <some HEAD before all your bad commits>, sau đó git rebase sẽ làm phẳng chúng (và bằng cách này, nếu bạn sử dụng yêu cầu kéo GitHub, sẽ có một tấn cam kết hợp nhất trong lịch sử của bạn). Điều này có thể rất thường dẫn đến lịch sử rất khác nhau (vì các thay đổi trùng lặp có thể bị "loại bỏ"), và trong trường hợp xấu nhất, nó có thể dẫn đến git rebase yêu cầu bạn giải quyết xung đột hợp nhất khó khăn (có khả năng đã được giải quyết trong các cam kết hợp nhất). Giải pháp là sử dụng -p gắn cờ git rebase, sẽ bảo tồn cấu trúc hợp nhất của lịch sử của bạn. Trang manpage cho git rebase cảnh báo rằng việc sử dụng -p và -i có thể dẫn đến các vấn đề, nhưng trong BUGS phần nó nói "Chỉnh sửa các cam kết và viết lại thông điệp cam kết của họ sẽ hoạt động tốt."

Tôi đã thêm -p vào lệnh trên. Đối với trường hợp bạn chỉ thay đổi cam kết gần đây nhất, đây không phải là vấn đề.


1417
2017-08-24 03:08



Tuyệt vời cho các cam kết lẻ mặc dù - hữu ích nếu bạn đang ghép nối và quên thay đổi tác giả - mloughran
+1 cho việc đề cập đến usecase để sửa lỗi một lỗi điển hình: git commit --amend --author = username - Nathan Kidd
Điều này là hoàn hảo, cách sử dụng phổ biến nhất của tôi là tôi ngồi ở một máy tính khác và quên thiết lập tác giả và do đó thường có <5 cam kết để sửa lỗi. - Zitrax
git commit --amend --reset-author cũng hoạt động một lần user.name và user.email được cấu hình đúng. - pts
Viết lại thông tin tác giả trên tất cả các cam kết sau <commit> sử dụng user.name và user.email từ ~/.gitconfig: chạy git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', lưu, thoát. Không cần chỉnh sửa! - ntc2


Bạn cũng có thể làm:

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

Lưu ý, nếu bạn đang sử dụng lệnh này trong dấu nhắc lệnh Windows, thì bạn cần sử dụng "thay vì ':

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

566
2018-05-15 19:15



Không sử dụng env-lọc giải pháp dễ dàng hơn? Bạn không chắc chắn tại sao điều này lại nhận được nhiều phiếu bầu hơn. - stigkj
Sau đó, liên kết bị hỏng. Làm thế nào để chúng tôi đẩy những thay đổi này vào kho lưu trữ khác? - Russell
env-filter sẽ thay đổi tất cả các commit. Giải pháp này cho phép một điều kiện. - user208769
"A previous backup already exists in refs/original/ Force overwriting the backup with -f" xin lỗi nhưng ở đâu -f -flag sẽ được thực hiện kịch bản này hai lần. Trên thực tế đó là trong câu trả lời của Brian, xin lỗi về sự xáo trộn ngay sau khi bộ lọc-chi nhánh là giải pháp. - hhh
@ user208769 env-filter cũng cho phép điều kiện; nhìn vào câu trả lời của tôi :-) - stigkj


Một lớp lót, nhưng hãy cẩn thận nếu bạn có kho lưu trữ nhiều người dùng - điều này sẽ thay đổi tất cả các cam kết có cùng một tác giả và người mới.

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

Với các dấu ngắt dòng trong chuỗi (có thể trong 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

483
2018-04-15 03:22



Có, nhưng đừng quên tên / email của người gửi thư. Điều gì làm việc cho tôi là git filter-branch -f --env-filter "GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='newemail'; GIT_COMMITER_NAME='Newname'; GIT_COMMITTER_EMAIL='newemail';" HEAD Nếu không, git sẽ theo dõi tên cũ với tư cách là người giao dịch! - Olivier Verdier
@Olivier Bạn có lỗi đánh máy trên GIT_COMMITER_NAME -> GIT_COMMITTER_NAME - Filipe Correia
Nó làm việc cho tất cả các cam kết cho tôi, bao gồm cả cam kết ban đầu. - Vincent
Tại sao nó viết lại tất cả các cam kết nếu bạn chỉ định HEAD ở cuối lệnh? - Nick Volynkin
Điều này không làm việc cho kho lưu trữ bitbucket của tôi, bất kỳ ý tưởng nào? Tôi làm một git push --force --tags origin 'refs/heads/*' sau lệnh được thông báo - ujsgeyrr1f0d0d0r0h1h0j0j_juj


Nó xảy ra khi bạn không có $ HOME / .gitconfig được khởi tạo. Bạn có thể sửa lỗi này thành:

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

thử nghiệm với phiên bản git 1.7.5.4


202
2018-02-16 09:46



Điều đó hoạt động thực sự tốt trên cam kết cuối cùng. Đẹp và đơn giản. Không có là một thay đổi toàn cầu, sử dụng --local cũng hoạt động - Ben
git commit --amend --reset-author --no-edit - mafrosis


Đối với một cam kết duy nhất:

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

(trích xuất từ ​​câu trả lời của asmeurer)


180
2018-04-26 22:50



nhưng đó chỉ là nếu đó là cam kết mới nhất - Richard
Theo git help commit, git commit --amend thay đổi cam kết tại “đầu của nhánh hiện tại” (là HEAD). Đây thường là cam kết mới nhất, nhưng bạn có thể thực hiện bất kỳ cam kết nào bạn muốn trước tiên kiểm tra cam kết với git checkout <branch-name> hoặc là git checkout <commit-SHA>. - Rory O'Kane
Nhưng nếu bạn làm điều đó, tất cả các cam kết đã có cam kết đó với tư cách là một phụ huynh sẽ chỉ đến sai cam kết. Tốt hơn nên sử dụng nhánh lọc tại thời điểm đó. - John Gietzen
@ JohnGietzen: Bạn có thể rebase các cam kết trở lại vào một trong đó là thay đổi để sửa chữa đó. Tuy nhiên, nếu bạn đang thực hiện> 1 cam kết, thì như đã đề cập, bộ lọc nhánh có lẽ sẽ dễ dàng hơn nhiều. - Thanatos
Lưu ý rằng thay đổi này chỉ cam kết author chứ không phải committer - Nick Volynkin


Trong trường hợp chỉ vài commit đầu có tác giả xấu, bạn có thể làm điều này bên trong git rebase -i sử dụng exec lệnh và --amend cam kết, như sau:

git rebase -i HEAD~6 # as required

trình bày cho bạn danh sách các cam kết có thể chỉnh sửa:

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

Sau đó thêm exec ... --author="..." dòng sau tất cả các dòng có tác giả xấu:

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

lưu và thoát khỏi trình soạn thảo (để chạy).

Giải pháp này có thể dài hơn so với một số khác, nhưng nó có khả năng kiểm soát cao - tôi biết chính xác những gì nó đạt được.

Nhờ @asmeurer cho cảm hứng.


152
2017-12-08 17:05



Chắc chắn tuyệt vời. Bạn có thể rút ngắn nó bằng cách đặt user.name và user.email trong cấu hình cục bộ của repo, và sau đó mỗi dòng chỉexec git commit --amend --reset-author -C HEAD ? - Andrew
@Andrew --reset-author hoạt động tốt. - Boggin
Câu trả lời kinh điển, để sử dụng bộ lọc-chi nhánh, chỉ cần xóa refs / heads / master cho tôi. Vì vậy, +1 với giải pháp có thể chỉnh sửa, có thể chỉnh sửa của bạn. Cảm ơn! - jmtd
Tại sao bạn bắt đầu với Someone else's committhay vì my bad commit 1? Tôi chỉ cố gắng HEAD^^ để sửa đổi 2 cam kết cuối cùng, và nó hoạt động hoàn toàn tốt đẹp. - fredoverflow
Thay cho git rebase -i HEAD^^^^^^ bạn cũng có thể viết git rebase -i HEAD~6 - Patrick Schlüter


Github có một giải pháp tốt đẹp, là kịch bản lệnh shell sau:

#!/bin/sh

git filter-branch --env-filter '

an="$GIT_AUTHOR_NAME"
am="$GIT_AUTHOR_EMAIL"
cn="$GIT_COMMITTER_NAME"
cm="$GIT_COMMITTER_EMAIL"

if [ "$GIT_COMMITTER_EMAIL" = "your@email.to.match" ]
then
    cn="Your New Committer Name"
    cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "your@email.to.match" ]
then
    an="Your New Author Name"
    am="Your New Author Email"
fi

export GIT_AUTHOR_NAME="$an"
export GIT_AUTHOR_EMAIL="$am"
export GIT_COMMITTER_NAME="$cn"
export GIT_COMMITTER_EMAIL="$cm"
'

108
2017-10-07 09:54



Làm việc hoàn hảo. Chỉ cần phải git reset --hard HEAD^ một vài lần trên các kho lưu trữ cục bộ khác để đưa chúng đến phiên bản cũ hơn, git pullphiên bản sửa đổi, và ở đây tôi không có bất kỳ dòng nào có chứa unknown <stupid-windows-user@.StupidWindowsDomain.local>(đã yêu mặc định của git). - Alan Plum
Tôi không thể đẩy sau này. Tôi có phải sử dụng "-f" không? - fossilet
tôi đã làm git push -f. Ngoài ra, repos địa phương phải được recloned sau này. - fossilet
Nếu bạn cần chạy kịch bản lệnh shell trên một nhánh cụ thể, bạn có thể thay đổi dòng cuối cùng thành: "'master..your-branch-name" (giả sử bạn phân nhánh master). - Robert Kajic
Nhấp vào liên kết <giải pháp tốt đẹp> vì tập lệnh đã được cập nhật - mdn


Như docgnome đã đề cập, viết lại lịch sử là nguy hiểm và sẽ phá vỡ kho của người khác.

Nhưng nếu bạn thực sự muốn làm điều đó và bạn đang ở trong một môi trường bash (không có vấn đề trong Linux, trên Windows, bạn có thể sử dụng git bash, được cung cấp với cài đặt git), sử dụng 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'

Để tăng tốc mọi thứ, bạn có thể chỉ định một loạt các bản chỉnh sửa bạn muốn viết lại:

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

79
2017-08-04 00:52



Lưu ý rằng điều này sẽ để lại bất kỳ thẻ nào trỏ vào các cam kết cũ. --tag-name-filter cat là tùy chọn "làm cho nó hoạt động". - Roman Starkov
@romkyns bất kỳ ý tưởng về cách thay đổi thẻ là tốt? - Nick Volynkin
@NickVolynkin Có, bạn chỉ định --tag-name-filter cat. Điều này thực sự phải là hành vi mặc định. - Roman Starkov