rebase用法 - git remove file from history




如何在Git中更改多個提交的作者和提交者名稱以及電子郵件? (20)

我正在學校的計算機上編寫一個簡單的腳本,並將更改提交給Git(在我的pendrive中的repo中,從我家裡的計算機克隆)。 幾次提交後,我意識到我正在以root用戶身份提交內容。

有沒有辦法將這些提交的作者改成我的名字?


使用Interactive Rebase

你可以做到

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

然後將所有錯誤提交標記為rebase文件中的“edit”。 如果您還想更改第一次提交,則必須手動將其添加為rebase文件中的第一行(遵循其他行的格式)。 然後,當git要求你修改每個提交時,做

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

編輯或只關閉打開的編輯器,然後執行

git rebase --continue

繼續堅持。

你可以通過附加--no-edit跳過這裡完全打開編輯器,這樣命令將是:

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

單一提交

正如一些評論者所指出的,如果您只想更改最近的提交,則不需要rebase命令。 做就是了

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

這會將作者更改為指定的名稱,但是將在git config user.namegit config user.email中將提交者設置為已配置的用戶。 如果要將提交者設置為您指定的內容,則會設置作者和提交者:

 git -c user.name="New Author Name" -c user.email=[email protected].com commit --amend --reset-author

關於合併提交的注意事項

我原來的回答有一點點缺陷。 如果當前HEAD和你的<some HEAD before all your bad commits>之間的<some HEAD before all your bad commits>有任何合併提交<some HEAD before all your bad commits> ,那麼git rebase將展平它們(順便說一句,如果你使用GitHub拉取請求,將會有大量的合併承諾你的歷史)。 這通常會導致非常不同的歷史記錄(因為重複更改可能會“重新出現”),並且在最壞的情況下,它可能導致git rebase要求您解決困難的合併衝突(可能已在合併提交中解決) )。 解決方案是使用-p標誌來git rebase ,這將保留歷史記錄的合併結構。 git rebase的聯機幫助頁警告說使用-p-i會導致問題,但在BUGS部分中它說“編輯提交和重寫他們的提交消息應該可以正常工作”。

我在上面的命令中添加了-p 。 對於剛剛更改最新提交的情況,這不是問題。


  1. 運行git rebase -i <sha1 or ref of starting point>
  2. 使用edit (或e )標記要更改的所有提交
  3. 循環以下兩個命令,直到您處理完所有提交:

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

這將保留所有其他提交信息(包括日期)。 --reuse-message=HEAD選項可防止消息編輯器啟動。


Github有一個很好的解決方案 ,它是以下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"
'

這是@Brian版本的更精細版本:

要更改作者和提交者,您可以執行此操作(在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

您可能會收到以下錯誤之一:

  1. 臨時目錄已存在
  2. refs / original開始的Refs已經存在
    (這意味著先前已在存儲庫上運行了另一個filter-branch,然後在refs / original處備份了原始分支引用)

如果要在不考慮這些錯誤的情況下強制運行,請添加--force標誌:

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

可能需要對-- --all選項進行一些解釋:它使filter-branch適用於所有refs (包括所有分支)的所有修訂。 這意味著,例如,標籤也被重寫並在重寫的分支上可見。

常見的“錯誤”是使用HEAD ,這意味著只過濾當前分支上的所有修訂。 然後在重寫的分支中不存在標籤(或其他引用)。


你也可以這樣做:

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 "[email protected]";
        else
                git commit-tree "[email protected]";
        fi' HEAD

注意,如果在Windows命令提示符下使用此命令,則需要使用"而不是'

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 "[email protected]";
        else
                git commit-tree "[email protected]";
        fi" HEAD

使用交互式rebase,您可以在每次要更改的提交後放置修改命令。 例如:

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

如果只有前幾個提交有不好的作者,你可以使用exec命令和--amend提交在git rebase -i完成所有操作,如下所示:

git rebase -i HEAD~6 # as required

它為您提供了可編輯的提交列表:

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

然後在所有帶有錯誤作者的行之後添加exec ... --author="..."行:

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

保存並退出編輯器(運行)。

這個解決方案可能比其他解決方案更長,但它是高度可控的 - 我確切知道它所提交的提交內容。

感謝@asmeurer的靈感。


如果您使用Eclipse與EGit,那麼有一個非常簡單的解決方案。
假設:您在本地分支“local_master_user_x”中提交,由於用戶無效,無法將其推送到遠程分支“master”。

  1. 簽出遠程分支'master'
  2. 選擇“local_master_user_x”包含更改的項目/文件夾/文件
  3. 右鍵單擊 - 替換為 - 分支 - 'local_master_user_x'
  4. 再次提交這些更改,這次是正確的用戶並進入本地分支'master'
  5. 推送到遠程'主'

如果您要修復的提交是最新的,只有其中幾個,您可以使用git resetgit stash的組合在配置正確的名稱和電子郵件後再次提交它們。

序列將是這樣的(對於2個錯誤的提交,沒有掛起的更改):

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

對於單個提交:

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

(摘自asmeurer的回答)


我使用以下內容為整個存儲庫重寫作者,包括標記和所有分支:

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

然後,如過濾器分支MAN頁面中所述,刪除由filter-branch備份的所有原始引用(這是破壞性的,先備份):

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

我們今天遇到了一個問題,即作者姓名中的UTF8字符在構建服務器上造成了問題,因此我們不得不重寫歷史記錄來糾正這個問題。 採取的步驟是:

步驟1:根據以下說明在git中更改您的所有用戶名: https://help.github.com/articles/setting-your-username-in-git/https://help.github.com/articles/setting-your-username-in-git/

第2步:運行以下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

快速概述:將存儲庫簽出到臨時文件,簽出所有遠程分支,運行將重寫歷史記錄的腳本,強制推送新狀態,並告訴所有同事執行rebase pull以獲取更改。

我們在OS X上運行它時遇到了麻煩,因為它在某些方面搞砸了提交消息中的行結尾,所以我們不得不在之後的Linux機器上重新運行它。


我發現所提供的版本是積極的,特別是如果你從其他開發人員提交補丁,這將基本上竊取他們的代碼。

下面的版本適用於所有分支,並分別更改作者和comitter以防止這種情況。

感謝leif81的所有選項。

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

我通過攝取一個簡單的author-conv-file (格式與git-cvsimport格式相同)來改編這個solution 。 它的工作原理是更改所有分支中author-conv-file定義的所有用戶。

我們將它與cvs2git結合使用,將我們的存儲庫從cvs遷移到git。

即示例author-conv-file

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

劇本:

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

最快,最簡單的方法是使用git rebase的--exec參數:

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

這將創建一個如下所示的待辦事項列表:

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

這將自動完成,當你有數百次提交時,它可以正常工作。


正如docgnome所提到的,重寫歷史是危險的,並將打破其他人的存儲庫。

但是如果你真的想這樣做並且你處於bash環境中(在Linux上沒有問題,在Windows上,你可以使用git bash,這是隨git的安裝提供的),請使用git filter-branch

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

為了加快速度,您可以指定要重寫的一系列修訂:

git filter-branch --env-filter '
  if [ $GIT_AUTHOR_EMAIL = [email protected] ];
    then [email protected];
  fi;
export GIT_AUTHOR_EMAIL' HEAD~20..HEAD

當您沒有初始化$ HOME / .gitconfig時會發生這種情況。 你可以解決這個問題:

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

用git版本1.7.5.4測試


請注意,git存儲兩個不同的電子郵件地址,一個用於提交者 (提交更改的人),另一個用於作者 (編寫更改的人)。

大多數地方都不顯示提交者信息,但您可以使用git log -1 --format=%cn,%ce (或使用show而不是log來指定特定提交)來查看它。

雖然更改上次提交的作者就像git commit --amend --author "Author Name <[email protected]>"一樣簡單,但是沒有單行或參數對提交者信息執行相同的操作。

解決方案是(暫時或不臨時)更改您的用戶信息,然後修改提交,這將更新提交者到您當前的信息:

git config user.email [email protected] 
git commit --amend

如果您是此回購的唯一用戶,或者您不關心可能為其他用戶破壞回購,那麼是。如果你已經推送了這些提交並且它們存在於其他地方可以訪問它們的地方,那麼不,除非你不關心打破其他人的回購。問題是通過更改這些提交,您將生成新的SHA,這將導致它們被視為不同的提交。當其他人試圖引入這些已更改的提交時,歷史記錄會與kaboom不同。

此頁面http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html介紹瞭如何執行此操作。(我沒試過這麼YMMV)


我也想添加我的例子。我想用給定的參數創建一個bash_function。

這適用於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-rewrite-history