在使用 Git 时,我们经常需要修改本地提交记录,比如:修改最近一次提交、将多次小的 commit 合并成一个、或者批量修改提交者信息。

以下操作会修改 Git 历史记录(SHA 值会变化),请确保不要对已推送到共享仓库的提交执行这些操作。

修改最近一次提交

修改提交说明

git commit --amend

进入编辑器修改提交说明,保存退出即可。

追加文件到最近一次提交

先将修改的文件添加到暂存区,再运行 --amend

git add new_file.cpp
git commit --amend

也可以用 -a 自动暂存所有已跟踪文件的修改,但要注意不要提交多余文件:

git commit -a --amend

将文件从本次提交中移除

git reset HEAD~1
git checkout -- filename  # 要从本次提交移除的文件
git commit -m "new commit"

修改多个提交记录(交互式 Rebase)

使用 git rebase -i 可以对历史提交进行修改、重排、合并、拆分等操作。

例如修改最近 3 次提交:

git rebase -i HEAD~3

进入交互界面后,会看到提交列表(注意顺序与 git log 相反,最早的在上面):

pick fecb551 Init the view model
pick bb199a0 Update the version
pick bc5cd9d Add new method

# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell

修改指定提交说明

pick 改为 reword

reword fecb551 Init the view model
pick bb199a0 Update the version
pick bc5cd9d Add new method

保存退出后,rebase 会在该提交处打开编辑器让你修改说明。

如果还需要修改提交内容,使用 edit

edit fecb551 Init the view model
pick bb199a0 Update the version
pick bc5cd9d Add new method

rebase 会在该提交处暂停,完成修改后运行:

git commit --amend
git rebase --continue

重排或删除提交

直接调整行的顺序或删除对应行即可。例如删除 “Update the version” 并交换其余两次提交的顺序:

pick bc5cd9d Add new method
pick fecb551 Init the view model

合并提交(Squash)

pick 改为 squash,该提交会被合并到上一个提交中:

pick fecb551 Init the view model
squash bb199a0 Update the version
squash bc5cd9d Add new method

保存退出后,rebase 会让你编辑合并后的提交说明。

拆分提交

使用 edit 暂停在要拆分的提交处,然后重置并分次提交:

pick fecb551 Init the view model
edit bb199a0 Update the version
pick bc5cd9d Add new method

rebase 暂停后执行:

git reset HEAD^
git add file1
git commit -m 'Update the version1'
git add file2
git commit -m 'Update the version2'
git rebase --continue

批量修改提交者信息

查看当前仓库中所有提交者信息:

git log --format='%aN %aE' | sort -u

使用 filter-branch 批量替换(如果 email 从未设置过,OLD_EMAIL 可以填 user.name):

git filter-branch -f --env-filter '
OLD_EMAIL="old@qq.com"
CORRECT_NAME="new_name"
CORRECT_EMAIL="new@gmail.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

修改历史后需要强制推送:

git push -f --tags

如果远程分支有保护策略(如 GitLab),需要先在 Project → Settings → Repository → Protected branches 中取消保护。

References

– EOF –